Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 856c345905 | |||
| b2cd5dfb85 | |||
| 462f7a59b5 | |||
| 22689d24ec | |||
| 4055afdf05 | |||
| a1c70a219a | |||
| 3151515855 | |||
| 25df9da715 | |||
| 9c2f438476 | |||
| 0de3be42cf | |||
| 1545e0eed7 | |||
| 42a7ed6877 | |||
| 98b8aedc4d | |||
| c7a2b54d1c | |||
| 6583ff4439 | |||
| 7b08b3e936 | |||
| bd2fc71dc5 | |||
| 0af97f63e2 | |||
| 1db635f24c | |||
| 6e27b3d6d4 | |||
| 1dea0edcf1 | |||
| 1abab41955 | |||
| d1be38be64 | |||
| 8916c68a0c | |||
| 6faa7e2fc9 | |||
| a6093f41bd | |||
| 107e66e4da | |||
| 256849f8a6 | |||
| c9c451be65 | |||
| 1fcea083e7 | |||
| 85d6ee5713 | |||
| ec81583741 | |||
| 332da9bf2d | |||
| a0036d925e | |||
| 81a99d6f7d | |||
| e912238574 | |||
| 9a830e5de6 | |||
| 7d9ba0e36e | |||
| 6c5e09fd84 | |||
| 2f8c43c689 | |||
| 7a25cb52b2 | |||
| 19bc97a7fd | |||
| 72900ef3b3 | |||
| 74ddd09c5d | |||
| e461cb92da | |||
| e91b53e66d | |||
| b5e8cb29dd | |||
| 20dc4f5bc4 | |||
| 2c399860ca | |||
| 4dc4c36581 | |||
| fe774f557c | |||
| 3ecb9d0f86 | |||
| f78a425985 | |||
| 9f365727e0 | |||
| 74d73cfe94 | |||
| 7fb9d430a9 | |||
| 7cd8ba13c0 | |||
| 1888888650 |
@@ -33,11 +33,12 @@
|
||||
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.68, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.4.0, 5.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.3.2, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.5.1, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebHelpers" version="[3.2.3, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebPages.Data" version="[3.2.3, 4.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -72,7 +73,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\UmbracoExamine.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</providers>
|
||||
</siteMap>
|
||||
<httpRuntime xdt:Transform="InsertIfMissing" />
|
||||
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" fcnMode="Single" xdt:Transform="SetAttributes(targetFramework,fcnMode,maxRequestLength)" />
|
||||
<httpRuntime targetFramework="4.5" maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(targetFramework,fcnMode,maxRequestLength)" />
|
||||
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
|
||||
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
|
||||
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
<security xdt:Transform="InsertIfMissing">
|
||||
<requestFiltering xdt:Transform="InsertIfMissing">
|
||||
<requestLimits maxAllowedContentLength="1073741824" xdt:Transform="InsertIfMissing" />
|
||||
<requestLimits maxAllowedContentLength="52428800" xdt:Transform="InsertIfMissing" />
|
||||
</requestFiltering>
|
||||
</security>
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.3.5
|
||||
7.3.7
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.3.5")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.5")]
|
||||
[assembly: AssemblyFileVersion("7.3.7")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.7")]
|
||||
@@ -11,35 +11,35 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
|
||||
/// <remarks>
|
||||
/// 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> : 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");
|
||||
@@ -54,7 +54,9 @@ namespace Umbraco.Core.Cache
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity);
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
@@ -79,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");
|
||||
|
||||
@@ -113,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");
|
||||
|
||||
@@ -128,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");
|
||||
|
||||
@@ -158,8 +165,7 @@ namespace Umbraco.Core.Cache
|
||||
else if (_options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//if the repository allows caching a zero count, then check the zero count cache
|
||||
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
|
||||
if (zeroCount != null && zeroCount.Any() == false)
|
||||
if (HasZeroCountCache())
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] {};
|
||||
@@ -179,28 +185,27 @@ namespace Umbraco.Core.Cache
|
||||
return entityCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
{
|
||||
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
|
||||
return (zeroCount != null && zeroCount.Any() == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
@@ -216,7 +221,9 @@ namespace Umbraco.Core.Cache
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity);
|
||||
Cache.InsertCacheItem(cacheKey, () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -235,6 +242,7 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
|
||||
// to signify that there is a zero count cache
|
||||
//NOTE: Don't set expiry/sliding for a zero count
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
|
||||
}
|
||||
else
|
||||
@@ -247,20 +255,14 @@ namespace Umbraco.Core.Cache
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (localCopy.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy);
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
protected void SetCacheAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -9,11 +11,157 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache) : base(cache, new RepositoryCachePolicyOptions())
|
||||
private readonly Func<TEntity, TId> _getEntityId;
|
||||
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
|
||||
private readonly bool _expires;
|
||||
|
||||
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 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()
|
||||
? result.Where(x => ids.Contains(_getEntityId(x))).ToArray()
|
||||
: 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>
|
||||
@@ -21,37 +169,61 @@ 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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
{
|
||||
if (_hasZeroCountCache.HasValue)
|
||||
return _hasZeroCountCache.Value;
|
||||
|
||||
_hasZeroCountCache = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey()) != null;
|
||||
return _hasZeroCountCache.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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());
|
||||
|
||||
//This method will get called before checking for zero count cache, so we'll just set the flag here
|
||||
_hasZeroCountCache = found != null;
|
||||
|
||||
return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -11,15 +13,21 @@ namespace Umbraco.Core.Cache
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
|
||||
public FullDataSetRepositoryCachePolicyFactory(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, 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);
|
||||
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);
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Cache
|
||||
/// </remarks>
|
||||
public class IsolatedRuntimeCache
|
||||
{
|
||||
private readonly Func<Type, IRuntimeCacheProvider> _cacheFactory;
|
||||
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
|
||||
{
|
||||
_cacheFactory = cacheFactory;
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache<T>()
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => _cacheFactory(type));
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache(Type type)
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(type, t => _cacheFactory(t));
|
||||
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,8 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
private readonly IsolatedRuntimeCache _isolatedCacheManager;
|
||||
private readonly ICacheProvider _requestCache;
|
||||
private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
|
||||
private readonly ICacheProvider _staticCache;
|
||||
private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
|
||||
|
||||
/// <summary>
|
||||
@@ -90,45 +86,33 @@ namespace Umbraco.Core
|
||||
if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider");
|
||||
if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider");
|
||||
if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager");
|
||||
_runtimeCache = httpCacheProvider;
|
||||
_staticCache = staticCacheProvider;
|
||||
_requestCache = requestCacheProvider;
|
||||
_isolatedCacheManager = isolatedCacheManager;
|
||||
RuntimeCache = httpCacheProvider;
|
||||
StaticCache = staticCacheProvider;
|
||||
RequestCache = requestCacheProvider;
|
||||
IsolatedRuntimeCache = isolatedCacheManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Request cache
|
||||
/// </summary>
|
||||
public ICacheProvider RequestCache
|
||||
{
|
||||
get { return _requestCache; }
|
||||
}
|
||||
public ICacheProvider RequestCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache
|
||||
{
|
||||
get { return _staticCache; }
|
||||
}
|
||||
public ICacheProvider StaticCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
get { return _runtimeCache; }
|
||||
}
|
||||
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Isolated Runtime cache manager
|
||||
/// </summary>
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache
|
||||
{
|
||||
get { return _isolatedCacheManager; }
|
||||
}
|
||||
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
|
||||
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache
|
||||
@@ -137,8 +121,8 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearAllCache()
|
||||
{
|
||||
_runtimeCache.ClearAllCache();
|
||||
_isolatedCacheManager.ClearAllCaches();
|
||||
RuntimeCache.ClearAllCache();
|
||||
IsolatedRuntimeCache.ClearAllCaches();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -149,7 +133,7 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
_runtimeCache.ClearCacheItem(key);
|
||||
RuntimeCache.ClearCacheItem(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +145,7 @@ namespace Umbraco.Core
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
_runtimeCache.ClearCacheObjectTypes(typeName);
|
||||
RuntimeCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,7 +155,7 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
_runtimeCache.ClearCacheObjectTypes<T>();
|
||||
RuntimeCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,7 +166,7 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
_runtimeCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
RuntimeCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -193,14 +177,14 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
_runtimeCache.ClearCacheByKeyExpression(regexString);
|
||||
RuntimeCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
{
|
||||
return _runtimeCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
return RuntimeCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,7 +197,7 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey);
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -227,7 +211,7 @@ namespace Umbraco.Core
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
{
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
|
||||
}
|
||||
|
||||
@@ -244,7 +228,7 @@ namespace Umbraco.Core
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
TimeSpan timeout, Func<TT> getCacheItem)
|
||||
{
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
|
||||
}
|
||||
|
||||
@@ -263,7 +247,7 @@ namespace Umbraco.Core
|
||||
CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
|
||||
}
|
||||
|
||||
@@ -283,7 +267,7 @@ namespace Umbraco.Core
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
|
||||
}
|
||||
|
||||
@@ -306,7 +290,7 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
var cache = RuntimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
@@ -330,7 +314,7 @@ namespace Umbraco.Core
|
||||
CacheDependency cacheDependency,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
var cache = RuntimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
@@ -352,7 +336,7 @@ namespace Umbraco.Core
|
||||
CacheItemPriority priority,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
_runtimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
RuntimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
|
||||
}
|
||||
|
||||
@@ -371,7 +355,7 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
_runtimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
RuntimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -390,7 +374,7 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
var cache = RuntimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
@@ -416,7 +400,7 @@ namespace Umbraco.Core
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
var cache = RuntimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
|
||||
@@ -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.5");
|
||||
private static readonly Version Version = new Version("7.3.7");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -136,7 +136,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationInitialized", x.GetType())))
|
||||
{
|
||||
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -299,7 +302,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationStarting", x.GetType())))
|
||||
{
|
||||
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -350,7 +356,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationStarted", x.GetType())))
|
||||
{
|
||||
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Web;
|
||||
@@ -12,6 +13,12 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public class DisposableTimer : DisposableObject
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly LogType? _logType;
|
||||
private readonly IProfiler _profiler;
|
||||
private readonly Type _loggerType;
|
||||
private readonly string _endMessage;
|
||||
private readonly IDisposable _profilerStep;
|
||||
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
|
||||
private readonly Action<long> _callback;
|
||||
|
||||
@@ -25,25 +32,12 @@ namespace Umbraco.Core
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
if (loggerType == null) throw new ArgumentNullException("loggerType");
|
||||
|
||||
_callback = x =>
|
||||
{
|
||||
if (profiler != null)
|
||||
{
|
||||
profiler.DisposeIfDisposable();
|
||||
}
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
logger.Debug(loggerType, () => endMessage + " (took " + x + "ms)");
|
||||
break;
|
||||
case LogType.Info:
|
||||
logger.Info(loggerType, () => endMessage + " (took " + x + "ms)");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("logType");
|
||||
}
|
||||
|
||||
};
|
||||
_logger = logger;
|
||||
_logType = logType;
|
||||
_profiler = profiler;
|
||||
_loggerType = loggerType;
|
||||
_endMessage = endMessage;
|
||||
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
@@ -58,7 +52,7 @@ namespace Umbraco.Core
|
||||
|
||||
if (profiler != null)
|
||||
{
|
||||
profiler.Step(loggerType, startMessage);
|
||||
_profilerStep = profiler.Step(loggerType, startMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +217,36 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_callback.Invoke(Stopwatch.ElapsedMilliseconds);
|
||||
if (_profiler != null)
|
||||
{
|
||||
_profiler.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
if (_profilerStep != null)
|
||||
{
|
||||
_profilerStep.Dispose();
|
||||
}
|
||||
|
||||
if (_logType.HasValue && _endMessage.IsNullOrWhiteSpace() == false && _loggerType != null && _logger != null)
|
||||
{
|
||||
switch (_logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
_logger.Debug(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
|
||||
break;
|
||||
case LogType.Info:
|
||||
_logger.Info(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("logType");
|
||||
}
|
||||
}
|
||||
|
||||
if (_callback != null)
|
||||
{
|
||||
_callback.Invoke(Stopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the default Template.
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public ITemplate DefaultTemplate
|
||||
@@ -79,6 +82,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<ITemplate> AllowedTemplates
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -18,8 +18,11 @@ namespace Umbraco.Core.Persistence.Relators
|
||||
// Is this the same DictionaryItem as the current one we're processing
|
||||
if (Current != null && Current.Id == a.Id)
|
||||
{
|
||||
// Yes, just add this User2AppDto to the current item's collection
|
||||
Current.User2AppDtos.Add(p);
|
||||
if (p.AppAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
// Yes, just add this User2AppDto to the current item's collection
|
||||
Current.User2AppDtos.Add(p);
|
||||
}
|
||||
|
||||
// Return null to indicate we're not done with this User yet
|
||||
return null;
|
||||
@@ -35,7 +38,7 @@ namespace Umbraco.Core.Persistence.Relators
|
||||
Current = a;
|
||||
Current.User2AppDtos = new List<User2AppDto>();
|
||||
//this can be null since we are doing a left join
|
||||
if (p.AppAlias != null)
|
||||
if (p.AppAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
Current.User2AppDtos.Add(p);
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id",
|
||||
"DELETE FROM umbracoAccess WHERE nodeId = @Id"
|
||||
"DELETE FROM umbracoAccess WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax);
|
||||
{
|
||||
}
|
||||
|
||||
private readonly GuidReadOnlyContentTypeBaseRepository _guidRepo;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the content type ids that match the query
|
||||
/// </summary>
|
||||
@@ -565,14 +562,13 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<IMediaType> GetMediaTypes<TRepo, TId>(
|
||||
TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
public static IEnumerable<IMediaType> GetMediaTypes<TRepo>(
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId: struct
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
IDictionary<TId, IEnumerable<TId>> allParentMediaTypeIds;
|
||||
var mediaTypes = MapMediaTypes(mediaTypeIds, db, sqlSyntax, out allParentMediaTypeIds)
|
||||
IDictionary<int, List<int>> allParentMediaTypeIds;
|
||||
var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds)
|
||||
.ToArray();
|
||||
|
||||
MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds);
|
||||
@@ -580,16 +576,15 @@ AND umbracoNode.id <> @id",
|
||||
return mediaTypes;
|
||||
}
|
||||
|
||||
public static IEnumerable<IContentType> GetContentTypes<TRepo, TId>(
|
||||
TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
public static IEnumerable<IContentType> GetContentTypes<TRepo>(
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository,
|
||||
ITemplateRepository templateRepository)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId : struct
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
IDictionary<TId, IEnumerable<AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<TId, IEnumerable<TId>> allParentContentTypeIds;
|
||||
var contentTypes = MapContentTypes(contentTypeIds, db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
IDictionary<int, List<AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<int, List<int>> allParentContentTypeIds;
|
||||
var contentTypes = MapContentTypes(db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
.ToArray();
|
||||
|
||||
if (contentTypes.Any())
|
||||
@@ -604,12 +599,11 @@ AND umbracoNode.id <> @id",
|
||||
return contentTypes;
|
||||
}
|
||||
|
||||
internal static void MapContentTypeChildren<TRepo, TId>(IContentTypeComposition[] contentTypes,
|
||||
internal static void MapContentTypeChildren<TRepo>(IContentTypeComposition[] contentTypes,
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository,
|
||||
IDictionary<TId, IEnumerable<TId>> allParentContentTypeIds)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId : struct
|
||||
IDictionary<int, List<int>> allParentContentTypeIds)
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
//NOTE: SQL call #2
|
||||
|
||||
@@ -631,20 +625,17 @@ AND umbracoNode.id <> @id",
|
||||
var allParentIdsAsArray = allParentContentTypeIds.SelectMany(x => x.Value).Distinct().ToArray();
|
||||
if (allParentIdsAsArray.Any())
|
||||
{
|
||||
var allParentContentTypes = contentTypeRepository.GetAll(allParentIdsAsArray).ToArray();
|
||||
var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray();
|
||||
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
|
||||
{
|
||||
var entityId = contentType.Id;
|
||||
|
||||
var parentContentTypes = allParentContentTypes.Where(x =>
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var parentEntityId = typeof(TId) == typeof(int) ? x.Id : (object)x.Key;
|
||||
var parentEntityId = x.Id;
|
||||
|
||||
return allParentContentTypeIds[(TId)entityId].Contains((TId)parentEntityId);
|
||||
return allParentContentTypeIds[entityId].Contains(parentEntityId);
|
||||
});
|
||||
foreach (var parentContentType in parentContentTypes)
|
||||
{
|
||||
@@ -662,13 +653,12 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
internal static void MapContentTypeTemplates<TRepo, TId>(IContentType[] contentTypes,
|
||||
internal static void MapContentTypeTemplates<TRepo>(IContentType[] contentTypes,
|
||||
Database db,
|
||||
TRepo contentTypeRepository,
|
||||
ITemplateRepository templateRepository,
|
||||
IDictionary<TId, IEnumerable<AssociatedTemplate>> associatedTemplates)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId: struct
|
||||
IDictionary<int, List<AssociatedTemplate>> associatedTemplates)
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
if (associatedTemplates == null || associatedTemplates.Any() == false) return;
|
||||
|
||||
@@ -685,11 +675,9 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
|
||||
|
||||
var associatedTemplateIds = associatedTemplates[(TId)entityId].Select(x => x.TemplateId)
|
||||
var entityId = contentType.Id;
|
||||
|
||||
var associatedTemplateIds = associatedTemplates[entityId].Select(x => x.TemplateId)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
@@ -701,19 +689,14 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
internal static IEnumerable<IMediaType> MapMediaTypes<TId>(TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<TId, IEnumerable<TId>> parentMediaTypeIds)
|
||||
where TId : struct
|
||||
internal static IEnumerable<IMediaType> MapMediaTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<int, List<int>> parentMediaTypeIds)
|
||||
{
|
||||
Mandate.That(mediaTypeIds.Any(), () => new InvalidOperationException("must be at least one content type id specified"));
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
//ensure they are unique
|
||||
mediaTypeIds = mediaTypeIds.Distinct().ToArray();
|
||||
|
||||
|
||||
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -735,27 +718,10 @@ AND umbracoNode.id <> @id",
|
||||
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
|
||||
) ParentTypes
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
|
||||
|
||||
if (mediaTypeIds.Any())
|
||||
{
|
||||
//TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
|
||||
// work for the time being.
|
||||
if (typeof(TId) == typeof(int))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
|
||||
}
|
||||
else if (typeof(TId) == typeof(Guid))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
|
||||
if ((mediaTypeIds.Length - 1) > 2000)
|
||||
throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters");
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType), contentTypeIds = mediaTypeIds });
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType) });
|
||||
|
||||
if (result.Any() == false)
|
||||
{
|
||||
@@ -763,91 +729,114 @@ AND umbracoNode.id <> @id",
|
||||
return Enumerable.Empty<IMediaType>();
|
||||
}
|
||||
|
||||
parentMediaTypeIds = new Dictionary<TId, IEnumerable<TId>>();
|
||||
parentMediaTypeIds = new Dictionary<int, List<int>>();
|
||||
var mappedMediaTypes = new List<IMediaType>();
|
||||
|
||||
foreach (var contentTypeId in mediaTypeIds)
|
||||
//loop through each result and fill in our required values, each row will contain different requried data than the rest.
|
||||
// it is much quicker to iterate each result and populate instead of looking up the values over and over in the result like
|
||||
// we used to do.
|
||||
var queue = new Queue<dynamic>(result);
|
||||
var currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
//the current content type id that we're working with
|
||||
var ct = queue.Dequeue();
|
||||
|
||||
var currentCtId = contentTypeId;
|
||||
|
||||
//first we want to get the main content type data this is 1 : 1 with umbraco node data
|
||||
|
||||
var ct = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof (TId) == typeof (int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
|
||||
.DistinctBy(x => (int)x.ctId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (ct == null)
|
||||
//check for allowed content types
|
||||
int? allowedCtId = ct.ctaAllowedId;
|
||||
int? allowedCtSort = ct.ctaSortOrder;
|
||||
string allowedCtAlias = ct.ctaAlias;
|
||||
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
|
||||
{
|
||||
continue;
|
||||
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
|
||||
if (currAllowedContentTypes.Contains(ctSort) == false)
|
||||
{
|
||||
currAllowedContentTypes.Add(ctSort);
|
||||
}
|
||||
}
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
//always ensure there's a list for this content type
|
||||
if (parentMediaTypeIds.ContainsKey(ct.ctId) == false)
|
||||
parentMediaTypeIds[ct.ctId] = new List<int>();
|
||||
|
||||
//check for parent ids and assign to the outgoing collection
|
||||
int? parentId = ct.chtParentId;
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
Alias = ct.ctAlias,
|
||||
AllowAtRoot = ct.ctAllowAtRoot,
|
||||
Description = ct.ctDesc,
|
||||
Icon = ct.ctIcon,
|
||||
IsContainer = ct.ctIsContainer,
|
||||
NodeId = ct.ctId,
|
||||
PrimaryKey = ct.ctPk,
|
||||
Thumbnail = ct.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = ct.nCreateDate,
|
||||
Level = (short)ct.nLevel,
|
||||
NodeId = ct.ctId,
|
||||
NodeObjectType = ct.nObjectType,
|
||||
ParentId = ct.nParentId,
|
||||
Path = ct.nPath,
|
||||
SortOrder = ct.nSortOrder,
|
||||
Text = ct.nName,
|
||||
Trashed = ct.nTrashed,
|
||||
UniqueId = ct.nUniqueId,
|
||||
UserId = ct.nUser
|
||||
}
|
||||
};
|
||||
var associatedParentIds = parentMediaTypeIds[ct.ctId];
|
||||
if (associatedParentIds.Contains(parentId.Value) == false)
|
||||
associatedParentIds.Add(parentId.Value);
|
||||
}
|
||||
|
||||
//now create the media type object
|
||||
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
|
||||
{
|
||||
//it's the last in the queue or the content type is changing (moving to the next one)
|
||||
var mediaType = CreateForMapping(ct, currAllowedContentTypes);
|
||||
mappedMediaTypes.Add(mediaType);
|
||||
|
||||
var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType));
|
||||
var mediaType = factory.BuildEntity(contentTypeDto);
|
||||
|
||||
//map the allowed content types
|
||||
//map the child content type ids
|
||||
MapCommonContentTypeObjects(mediaType, currentCtId, result, parentMediaTypeIds);
|
||||
|
||||
mappedMediaTypes.Add(mediaType);
|
||||
//Here we need to reset the current variables, we're now collecting data for a different content type
|
||||
currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
}
|
||||
}
|
||||
|
||||
return mappedMediaTypes;
|
||||
}
|
||||
|
||||
internal static IEnumerable<IContentType> MapContentTypes<TId>(TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<TId, IEnumerable<AssociatedTemplate>> associatedTemplates,
|
||||
out IDictionary<TId, IEnumerable<TId>> parentContentTypeIds)
|
||||
where TId : struct
|
||||
private static IMediaType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes)
|
||||
{
|
||||
// * create the DTO object
|
||||
// * create the content type object
|
||||
// * map the allowed content types
|
||||
// * add to the outgoing list
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
{
|
||||
Alias = currCt.ctAlias,
|
||||
AllowAtRoot = currCt.ctAllowAtRoot,
|
||||
Description = currCt.ctDesc,
|
||||
Icon = currCt.ctIcon,
|
||||
IsContainer = currCt.ctIsContainer,
|
||||
NodeId = currCt.ctId,
|
||||
PrimaryKey = currCt.ctPk,
|
||||
Thumbnail = currCt.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = currCt.nCreateDate,
|
||||
Level = (short)currCt.nLevel,
|
||||
NodeId = currCt.ctId,
|
||||
NodeObjectType = currCt.nObjectType,
|
||||
ParentId = currCt.nParentId,
|
||||
Path = currCt.nPath,
|
||||
SortOrder = currCt.nSortOrder,
|
||||
Text = currCt.nName,
|
||||
Trashed = currCt.nTrashed,
|
||||
UniqueId = currCt.nUniqueId,
|
||||
UserId = currCt.nUser
|
||||
}
|
||||
};
|
||||
|
||||
//now create the content type object
|
||||
|
||||
var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType));
|
||||
var mediaType = factory.BuildEntity(contentTypeDto);
|
||||
|
||||
//map the allowed content types
|
||||
mediaType.AllowedContentTypes = currAllowedContentTypes;
|
||||
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<int, List<AssociatedTemplate>> associatedTemplates,
|
||||
out IDictionary<int, List<int>> parentContentTypeIds)
|
||||
{
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
//ensure they are unique
|
||||
contentTypeIds = contentTypeIds.Distinct().ToArray();
|
||||
|
||||
|
||||
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
|
||||
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -878,28 +867,10 @@ AND umbracoNode.id <> @id",
|
||||
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
|
||||
) ParentTypes
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
|
||||
|
||||
if (contentTypeIds.Any())
|
||||
{
|
||||
//TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
|
||||
// work for the time being.
|
||||
if (typeof(TId) == typeof(int))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
|
||||
}
|
||||
else if (typeof(TId) == typeof(Guid))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
|
||||
if ((contentTypeIds.Length - 1) > 2000)
|
||||
throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters");
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), contentTypeIds = contentTypeIds });
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType)});
|
||||
|
||||
if (result.Any() == false)
|
||||
{
|
||||
@@ -908,121 +879,141 @@ AND umbracoNode.id <> @id",
|
||||
return Enumerable.Empty<IContentType>();
|
||||
}
|
||||
|
||||
parentContentTypeIds = new Dictionary<TId, IEnumerable<TId>>();
|
||||
associatedTemplates = new Dictionary<TId, IEnumerable<AssociatedTemplate>>();
|
||||
parentContentTypeIds = new Dictionary<int, List<int>>();
|
||||
associatedTemplates = new Dictionary<int, List<AssociatedTemplate>>();
|
||||
var mappedContentTypes = new List<IContentType>();
|
||||
|
||||
foreach (var contentTypeId in contentTypeIds)
|
||||
var queue = new Queue<dynamic>(result);
|
||||
var currDefaultTemplate = -1;
|
||||
var currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
//the current content type id that we're working with
|
||||
var ct = queue.Dequeue();
|
||||
|
||||
var currentCtId = contentTypeId;
|
||||
|
||||
//first we want to get the main content type data this is 1 : 1 with umbraco node data
|
||||
|
||||
var ct = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
|
||||
.DistinctBy(x => (int)x.ctId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (ct == null)
|
||||
//check for default templates
|
||||
bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault);
|
||||
int? templateId = ct.dtTemplateId;
|
||||
if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && templateId.HasValue)
|
||||
{
|
||||
continue;
|
||||
currDefaultTemplate = templateId.Value;
|
||||
}
|
||||
|
||||
//get the unique list of associated templates
|
||||
var defaultTemplates = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
//use a tuple so that distinct checks both values (in some rare cases the dtIsDefault will not compute as bool?, so we force it with Convert.ToBoolean)
|
||||
.Select(x => new Tuple<bool?, int?>(Convert.ToBoolean(x.dtIsDefault), x.dtTemplateId))
|
||||
.Where(x => x.Item1.HasValue && x.Item2.HasValue)
|
||||
.Distinct()
|
||||
.OrderByDescending(x => x.Item1.Value)
|
||||
.ToArray();
|
||||
//if there isn't one set to default explicitly, we'll pick the first one
|
||||
var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value)
|
||||
?? defaultTemplates.FirstOrDefault();
|
||||
//always ensure there's a list for this content type
|
||||
if (associatedTemplates.ContainsKey(ct.ctId) == false)
|
||||
associatedTemplates[ct.ctId] = new List<AssociatedTemplate>();
|
||||
|
||||
var dtDto = new DocumentTypeDto
|
||||
//check for associated templates and assign to the outgoing collection
|
||||
if (ct.tId != null)
|
||||
{
|
||||
//create the content type dto
|
||||
ContentTypeDto = new ContentTypeDto
|
||||
var associatedTemplate = new AssociatedTemplate(ct.tId, ct.tAlias, ct.tText);
|
||||
var associatedList = associatedTemplates[ct.ctId];
|
||||
|
||||
if (associatedList.Contains(associatedTemplate) == false)
|
||||
associatedList.Add(associatedTemplate);
|
||||
}
|
||||
|
||||
//check for allowed content types
|
||||
int? allowedCtId = ct.ctaAllowedId;
|
||||
int? allowedCtSort = ct.ctaSortOrder;
|
||||
string allowedCtAlias = ct.ctaAlias;
|
||||
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
|
||||
{
|
||||
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
|
||||
if (currAllowedContentTypes.Contains(ctSort) == false)
|
||||
{
|
||||
Alias = ct.ctAlias,
|
||||
AllowAtRoot = ct.ctAllowAtRoot,
|
||||
Description = ct.ctDesc,
|
||||
Icon = ct.ctIcon,
|
||||
IsContainer = ct.ctIsContainer,
|
||||
NodeId = ct.ctId,
|
||||
PrimaryKey = ct.ctPk,
|
||||
Thumbnail = ct.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = ct.nCreateDate,
|
||||
Level = (short)ct.nLevel,
|
||||
NodeId = ct.ctId,
|
||||
NodeObjectType = ct.nObjectType,
|
||||
ParentId = ct.nParentId,
|
||||
Path = ct.nPath,
|
||||
SortOrder = ct.nSortOrder,
|
||||
Text = ct.nName,
|
||||
Trashed = ct.nTrashed,
|
||||
UniqueId = ct.nUniqueId,
|
||||
UserId = ct.nUser
|
||||
}
|
||||
},
|
||||
ContentTypeNodeId = ct.ctId,
|
||||
IsDefault = defaultTemplate != null,
|
||||
TemplateNodeId = defaultTemplate != null ? defaultTemplate.Item2.Value : 0,
|
||||
};
|
||||
currAllowedContentTypes.Add(ctSort);
|
||||
}
|
||||
}
|
||||
|
||||
// We will map a subset of the associated template - alias, id, name
|
||||
//always ensure there's a list for this content type
|
||||
if (parentContentTypeIds.ContainsKey(ct.ctId) == false)
|
||||
parentContentTypeIds[ct.ctId] = new List<int>();
|
||||
|
||||
associatedTemplates.Add(currentCtId, result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Where(x => x.tId != null)
|
||||
.Select(x => new AssociatedTemplate(x.tId, x.tAlias, x.tText))
|
||||
.Distinct()
|
||||
.ToArray());
|
||||
//check for parent ids and assign to the outgoing collection
|
||||
int? parentId = ct.chtParentId;
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
var associatedParentIds = parentContentTypeIds[ct.ctId];
|
||||
|
||||
//now create the content type object
|
||||
if (associatedParentIds.Contains(parentId.Value) == false)
|
||||
associatedParentIds.Add(parentId.Value);
|
||||
}
|
||||
|
||||
var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
|
||||
var contentType = factory.BuildEntity(dtDto);
|
||||
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
|
||||
{
|
||||
//it's the last in the queue or the content type is changing (moving to the next one)
|
||||
var contentType = CreateForMapping(ct, currAllowedContentTypes, currDefaultTemplate);
|
||||
mappedContentTypes.Add(contentType);
|
||||
|
||||
//map the allowed content types
|
||||
//map the child content type ids
|
||||
MapCommonContentTypeObjects(contentType, currentCtId, result, parentContentTypeIds);
|
||||
|
||||
mappedContentTypes.Add(contentType);
|
||||
//Here we need to reset the current variables, we're now collecting data for a different content type
|
||||
currDefaultTemplate = -1;
|
||||
currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
}
|
||||
}
|
||||
|
||||
return mappedContentTypes;
|
||||
}
|
||||
|
||||
private static IContentType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes, int currDefaultTemplate)
|
||||
{
|
||||
// * set the default template to the first one if a default isn't found
|
||||
// * create the DTO object
|
||||
// * create the content type object
|
||||
// * map the allowed content types
|
||||
// * add to the outgoing list
|
||||
|
||||
var dtDto = new DocumentTypeDto
|
||||
{
|
||||
//create the content type dto
|
||||
ContentTypeDto = new ContentTypeDto
|
||||
{
|
||||
Alias = currCt.ctAlias,
|
||||
AllowAtRoot = currCt.ctAllowAtRoot,
|
||||
Description = currCt.ctDesc,
|
||||
Icon = currCt.ctIcon,
|
||||
IsContainer = currCt.ctIsContainer,
|
||||
NodeId = currCt.ctId,
|
||||
PrimaryKey = currCt.ctPk,
|
||||
Thumbnail = currCt.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = currCt.nCreateDate,
|
||||
Level = (short)currCt.nLevel,
|
||||
NodeId = currCt.ctId,
|
||||
NodeObjectType = currCt.nObjectType,
|
||||
ParentId = currCt.nParentId,
|
||||
Path = currCt.nPath,
|
||||
SortOrder = currCt.nSortOrder,
|
||||
Text = currCt.nName,
|
||||
Trashed = currCt.nTrashed,
|
||||
UniqueId = currCt.nUniqueId,
|
||||
UserId = currCt.nUser
|
||||
}
|
||||
},
|
||||
ContentTypeNodeId = currCt.ctId,
|
||||
IsDefault = currDefaultTemplate != -1,
|
||||
TemplateNodeId = currDefaultTemplate != -1 ? currDefaultTemplate : 0,
|
||||
};
|
||||
|
||||
//now create the content type object
|
||||
|
||||
var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
|
||||
var contentType = factory.BuildEntity(dtDto);
|
||||
|
||||
// NOTE
|
||||
// that was done by the factory but makes little sense, moved here, so
|
||||
// now we have to reset dirty props again (as the factory does it) and yet,
|
||||
// we are not managing allowed templates... the whole thing is weird.
|
||||
((ContentType)contentType).DefaultTemplateId = dtDto.TemplateNodeId;
|
||||
contentType.ResetDirtyProperties(false);
|
||||
|
||||
//map the allowed content types
|
||||
contentType.AllowedContentTypes = currAllowedContentTypes;
|
||||
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private static void MapCommonContentTypeObjects<T, TId>(T contentType, TId currentCtId, List<dynamic> result, IDictionary<TId, IEnumerable<TId>> parentContentTypeIds)
|
||||
where T : IContentTypeBase
|
||||
where TId : struct
|
||||
@@ -1197,71 +1188,20 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inner repository to support the GUID lookups and keep the caching consistent
|
||||
/// </summary>
|
||||
internal class GuidReadOnlyContentTypeBaseRepository : PetaPocoRepositoryBase<Guid, TEntity>
|
||||
{
|
||||
private readonly ContentTypeBaseRepository<TEntity> _parentRepo;
|
||||
|
||||
public GuidReadOnlyContentTypeBaseRepository(
|
||||
ContentTypeBaseRepository<TEntity> parentRepo,
|
||||
IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_parentRepo = parentRepo;
|
||||
}
|
||||
|
||||
protected override TEntity PerformGet(Guid id)
|
||||
{
|
||||
return _parentRepo.PerformGet(id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<TEntity> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
return _parentRepo.PerformGetAll(ids);
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return _parentRepo.GetBaseQuery(isCount);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return "umbracoNode.uniqueID = @Id";
|
||||
}
|
||||
|
||||
#region No implementation required
|
||||
protected override IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(TEntity entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(TEntity entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
protected abstract TEntity PerformGet(Guid id);
|
||||
protected abstract TEntity PerformGet(string alias);
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params Guid[] ids);
|
||||
protected abstract bool PerformExists(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an Entity by alias
|
||||
/// </summary>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
public TEntity Get(string alias)
|
||||
{
|
||||
return PerformGet(alias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an Entity by Id
|
||||
@@ -1270,7 +1210,7 @@ AND umbracoNode.id <> @id",
|
||||
/// <returns></returns>
|
||||
public TEntity Get(Guid id)
|
||||
{
|
||||
return _guidRepo.Get(id);
|
||||
return PerformGet(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1278,9 +1218,12 @@ AND umbracoNode.id <> @id",
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<TEntity> GetAll(params Guid[] ids)
|
||||
/// <remarks>
|
||||
/// Ensure explicit implementation, we don't want to have any accidental calls to this since it is essentially the same signature as the main GetAll when there are no parameters
|
||||
/// </remarks>
|
||||
IEnumerable<TEntity> IReadRepository<Guid, TEntity>.GetAll(params Guid[] ids)
|
||||
{
|
||||
return _guidRepo.GetAll(ids);
|
||||
return PerformGetAll(ids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1290,7 +1233,7 @@ AND umbracoNode.id <> @id",
|
||||
/// <returns></returns>
|
||||
public bool Exists(Guid id)
|
||||
{
|
||||
return _guidRepo.Exists(id);
|
||||
return PerformExists(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -27,46 +28,54 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_templateRepository = templateRepository;
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,IContentType>
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IContentType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IContentType, int> CachePolicyFactory
|
||||
{
|
||||
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, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires:true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IContentType PerformGet(int id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetContentTypes(
|
||||
new[] {id}, Database, SqlSyntax, this, _templateRepository);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetAll(params int[] ids)
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetContentTypes(ids, Database, SqlSyntax, this, _templateRepository);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository);
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository)
|
||||
.Where(x => ids.Contains(x.Id));
|
||||
}
|
||||
|
||||
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetByQuery(IQuery<IContentType> query)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
/// </summary>
|
||||
@@ -244,25 +253,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IContentType PerformGet(Guid id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetContentTypes(
|
||||
new[] { id }, Database, SqlSyntax, this, _templateRepository);
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
protected override IContentType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetContentTypes(ids, Database, SqlSyntax, this, _templateRepository);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>(SqlSyntax).Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository);
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IDomain, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentTypeCompositionRepository<TEntity> : IRepositoryQueryable<int, TEntity>, IReadRepository<Guid, TEntity>
|
||||
where TEntity : IContentTypeComposition
|
||||
{
|
||||
TEntity Get(string alias);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentTypeRepository : IRepositoryQueryable<int, IContentType>, IReadRepository<Guid, IContentType>
|
||||
public interface IContentTypeRepository : IContentTypeCompositionRepository<IContentType>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaTypeRepository : IRepositoryQueryable<int, IMediaType>, IReadRepository<Guid, IMediaType>
|
||||
public interface IMediaTypeRepository : IContentTypeCompositionRepository<IMediaType>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
|
||||
@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMemberTypeRepository : IRepositoryQueryable<int, IMemberType>, IReadRepository<Guid, IMemberType>
|
||||
public interface IMemberTypeRepository : IContentTypeCompositionRepository<IMemberType>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ILanguage, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -24,46 +25,54 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,IMedia>
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMediaType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMediaType, int> CachePolicyFactory
|
||||
{
|
||||
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, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IMediaType PerformGet(int id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetMediaTypes(
|
||||
new[] { id }, Database, SqlSyntax, this);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetAll(params int[] ids)
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetMediaTypes(ids, Database, SqlSyntax, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetMediaTypes(allIds, Database, SqlSyntax, this);
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this)
|
||||
.Where(x => ids.Contains(x.Id));
|
||||
}
|
||||
|
||||
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetByQuery(IQuery<IMediaType> query)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
@@ -168,25 +177,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IMediaType PerformGet(Guid id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetMediaTypes(
|
||||
new[] { id }, Database, SqlSyntax, this);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetMediaTypes(ids, Database, SqlSyntax, this);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>(SqlSyntax).Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetMediaTypes(allIds, Database, SqlSyntax, this);
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
|
||||
protected override IMediaType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using log4net;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -26,25 +27,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int, IMemberType>
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMemberType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMemberType, int> CachePolicyFactory
|
||||
{
|
||||
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, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IMemberType PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
|
||||
if (dtos == null || dtos.Any() == false)
|
||||
return null;
|
||||
|
||||
var factory = new MemberTypeReadOnlyFactory();
|
||||
var member = factory.BuildEntity(dtos.First());
|
||||
|
||||
return member;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMemberType> PerformGetAll(params int[] ids)
|
||||
@@ -52,10 +51,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
if (ids.Any())
|
||||
{
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
|
||||
sql.Where(statement);
|
||||
}
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var subquery = translator.Translate();
|
||||
var sql = GetBaseQuery(false)
|
||||
.Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments))
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
@@ -79,11 +79,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return BuildFromDtos(dtos);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int, IMemberType>
|
||||
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
@@ -157,11 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
get { return new Guid(Constants.ObjectTypes.MemberType); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unit of Work Implementation
|
||||
|
||||
|
||||
protected override void PersistNewItem(IMemberType entity)
|
||||
{
|
||||
ValidateAlias(entity);
|
||||
@@ -238,8 +230,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Override so we can specify explicit db type's on any property types that are built-in.
|
||||
@@ -262,38 +253,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IMemberType PerformGet(Guid id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where("umbracoNode.uniqueID = @Id", new { Id = id });
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
|
||||
if (dtos == null || dtos.Any() == false)
|
||||
return null;
|
||||
|
||||
var factory = new MemberTypeReadOnlyFactory();
|
||||
var member = factory.BuildEntity(dtos.First());
|
||||
|
||||
return member;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMemberType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.uniqueID='{0}'", x)));
|
||||
sql.Where(statement);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
else
|
||||
{
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
|
||||
return BuildFromDtos(dtos);
|
||||
protected override IMemberType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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));
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual TId GetEntityId(TEntity entity)
|
||||
{
|
||||
return (TId)(object)entity.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The runtime cache used for this repo by default is the isolated cache for this type
|
||||
@@ -179,7 +183,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return p.GetAll(ids, PerformGetAll);
|
||||
var result = p.GetAll(ids, PerformGetAll);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ITemplate, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +476,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (aliases.Any() == false) return base.GetAll();
|
||||
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => aliases.Contains(x.Alias));
|
||||
return base.GetAll().Where(x => aliases.InvariantContains(x.Alias));
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetChildren(int masterTemplateId)
|
||||
@@ -488,7 +489,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var parent = all.FirstOrDefault(x => x.Id == masterTemplateId);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var children = all.Where(x => x.MasterTemplateAlias == parent.Alias);
|
||||
var children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias));
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -497,7 +498,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => alias.IsNullOrWhiteSpace()
|
||||
? x.MasterTemplateAlias.IsNullOrWhiteSpace()
|
||||
: x.MasterTemplateAlias == alias);
|
||||
: x.MasterTemplateAlias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetDescendants(int masterTemplateId)
|
||||
@@ -532,7 +533,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var descendants = new List<ITemplate>();
|
||||
if (alias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
var parent = all.FirstOrDefault(x => x.Alias == alias);
|
||||
var parent = all.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
//recursively add all children
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
@@ -552,7 +553,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private void AddChildren(ITemplate[] all, List<ITemplate> descendants, string masterAlias)
|
||||
{
|
||||
var c = all.Where(x => x.MasterTemplateAlias == masterAlias).ToArray();
|
||||
var c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray();
|
||||
descendants.AddRange(c);
|
||||
if (c.Any() == false) return;
|
||||
//recurse through all children
|
||||
@@ -573,7 +574,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//first get all template objects
|
||||
var allTemplates = base.GetAll().ToArray();
|
||||
|
||||
var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias == alias);
|
||||
var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
if (selfTemplate == null)
|
||||
{
|
||||
return null;
|
||||
@@ -582,11 +583,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var top = selfTemplate;
|
||||
while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
top = allTemplates.Single(x => x.Alias == top.MasterTemplateAlias);
|
||||
top = allTemplates.Single(x => x.Alias.InvariantEquals(top.MasterTemplateAlias));
|
||||
}
|
||||
|
||||
var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.Id));
|
||||
var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias == top.Alias);
|
||||
var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(top.Alias));
|
||||
//This now creates the hierarchy recursively
|
||||
topNode.Children = CreateChildren(topNode, childTemplates, allTemplates);
|
||||
|
||||
@@ -598,7 +599,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private static TemplateNode WalkTree(TemplateNode current, string alias)
|
||||
{
|
||||
//now walk the tree to find the node
|
||||
if (current.Template.Alias == alias)
|
||||
if (current.Template.Alias.InvariantEquals(alias))
|
||||
{
|
||||
return current;
|
||||
}
|
||||
@@ -730,7 +731,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//get this node's children
|
||||
var local = childTemplate;
|
||||
var kids = allTemplates.Where(x => x.MasterTemplateAlias == local.Alias);
|
||||
var kids = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(local.Alias));
|
||||
|
||||
//recurse
|
||||
child.Children = CreateChildren(child, kids, allTemplates);
|
||||
@@ -760,7 +761,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private bool AliasAlreadExists(ITemplate template)
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<TemplateDto>(x => x.Alias == template.Alias && x.NodeId != template.Id);
|
||||
var sql = GetBaseQuery(true).Where<TemplateDto>(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id);
|
||||
var count = Database.ExecuteScalar<int>(sql);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@@ -31,29 +31,27 @@ namespace Umbraco.Core.Persistence
|
||||
//if (sqlSyntax == null) throw new ArgumentNullException("sqlSyntax");
|
||||
if (settings == null) throw new ArgumentNullException("settings");
|
||||
|
||||
_cacheHelper = cacheHelper;
|
||||
_cacheHelper = cacheHelper;
|
||||
|
||||
//IMPORTANT: We will force the DeepCloneRuntimeCacheProvider to be used here which is a wrapper for the underlying
|
||||
// runtime cache to ensure that anything that can be deep cloned in/out is done so, this also ensures that our tracks
|
||||
// changes entities are reset.
|
||||
if ((_cacheHelper.RuntimeCache is DeepCloneRuntimeCacheProvider) == false)
|
||||
{
|
||||
var originalHelper = cacheHelper;
|
||||
|
||||
_cacheHelper = new CacheHelper(
|
||||
new DeepCloneRuntimeCacheProvider(originalHelper.RuntimeCache),
|
||||
originalHelper.StaticCache,
|
||||
originalHelper.RequestCache,
|
||||
new IsolatedRuntimeCache(type =>
|
||||
{
|
||||
var cache = originalHelper.IsolatedRuntimeCache.GetOrCreateCache(type);
|
||||
return (cache is DeepCloneRuntimeCacheProvider) == false
|
||||
//wrap the original if it's not DeepCloneRuntimeCacheProvider
|
||||
? new DeepCloneRuntimeCacheProvider(cache)
|
||||
: cache;
|
||||
}));
|
||||
var origRuntimeCache = cacheHelper.RuntimeCache;
|
||||
_cacheHelper.RuntimeCache = new DeepCloneRuntimeCacheProvider(origRuntimeCache);
|
||||
}
|
||||
|
||||
//If the factory for isolated cache doesn't return DeepCloneRuntimeCacheProvider, then ensure it does
|
||||
if (_cacheHelper.IsolatedRuntimeCache.CacheFactory.Method.ReturnType != typeof (DeepCloneRuntimeCacheProvider))
|
||||
{
|
||||
var origFactory = cacheHelper.IsolatedRuntimeCache.CacheFactory;
|
||||
_cacheHelper.IsolatedRuntimeCache.CacheFactory = type =>
|
||||
{
|
||||
var cache = origFactory(type);
|
||||
return new DeepCloneRuntimeCacheProvider(cache);
|
||||
};
|
||||
}
|
||||
|
||||
_noCache = CacheHelper.CreateDisabledCacheHelper();
|
||||
_logger = logger;
|
||||
_sqlSyntax = sqlSyntax;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -1227,6 +1227,13 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="userId">Optional Id of the user issueing the delete operation</param>
|
||||
public void DeleteContentOfType(int contentTypeId, int userId = 0)
|
||||
{
|
||||
//TODO: This currently this is called from the ContentTypeService but that needs to change,
|
||||
// if we are deleting a content type, we should just delete the data and do this operation slightly differently.
|
||||
// This method will recursively go lookup every content item, check if any of it's descendants are
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// The main problem with this is that for every content item being deleted, events are raised...
|
||||
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
|
||||
@@ -158,10 +158,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateContentTypeRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContentType>.Builder.Where(x => x.Alias == alias);
|
||||
var contentTypes = repository.GetByQuery(query);
|
||||
|
||||
return contentTypes.FirstOrDefault();
|
||||
return repository.Get(alias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +452,13 @@ namespace Umbraco.Core.Services
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
|
||||
//TODO: This needs to change, if we are deleting a content type, we should just delete the data,
|
||||
// this method will recursively go lookup every content item, check if any of it's descendants are
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// The main problem with this is that for every content item being deleted, events are raised...
|
||||
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
|
||||
|
||||
_contentService.DeleteContentOfType(contentType.Id);
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
@@ -531,10 +535,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateMediaTypeRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IMediaType>.Builder.Where(x => x.Alias == alias);
|
||||
var contentTypes = repository.GetByQuery(query);
|
||||
|
||||
return contentTypes.FirstOrDefault();
|
||||
return repository.Get(alias);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,10 +69,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateMemberTypeRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IMemberType>.Builder.Where(x => x.Alias == alias);
|
||||
var contentTypes = repository.GetByQuery(query);
|
||||
|
||||
return contentTypes.FirstOrDefault();
|
||||
return repository.Get(alias);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
protected override bool RequiresDistributed(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, MessageType dispatchType)
|
||||
{
|
||||
// we don't care if there's servers listed or not,
|
||||
// we don't care if there's servers listed or not,
|
||||
// if distributed call is enabled we will make the call
|
||||
return _initialized && DistributedEnabled;
|
||||
}
|
||||
@@ -139,12 +139,35 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
if (_released) return;
|
||||
|
||||
var coldboot = false;
|
||||
if (_lastId < 0) // never synced before
|
||||
{
|
||||
// we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
|
||||
// we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
|
||||
// server and it will need to rebuild it's own caches, eg Lucene or the xml cache file.
|
||||
_logger.Warn<DatabaseServerMessenger>("No last synced Id found, this generally means this is a new server/install. The server will rebuild its caches and indexes and then adjust it's last synced id to the latest found in the database and will start maintaining cache updates based on that id");
|
||||
_logger.Warn<DatabaseServerMessenger>("No last synced Id found, this generally means this is a new server/install."
|
||||
+ " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in"
|
||||
+ " the database and maintain cache updates based on that Id.");
|
||||
|
||||
coldboot = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//check for how many instructions there are to process
|
||||
var count = _appContext.DatabaseContext.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
|
||||
if (count > _options.MaxProcessingInstructionCount)
|
||||
{
|
||||
//too many instructions, proceed to cold boot
|
||||
_logger.Warn<DatabaseServerMessenger>("The instruction count ({0}) exceeds the specified MaxProcessingInstructionCount ({1})."
|
||||
+ " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id"
|
||||
+ " to the latest found in the database and maintain cache updates based on that Id.",
|
||||
() => count, () => _options.MaxProcessingInstructionCount);
|
||||
|
||||
coldboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (coldboot)
|
||||
{
|
||||
// go get the last id in the db and store it
|
||||
// note: do it BEFORE initializing otherwise some instructions might get lost
|
||||
// when doing it before, some instructions might run twice - not an issue
|
||||
@@ -169,13 +192,13 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_syncing)
|
||||
if (_syncing)
|
||||
return;
|
||||
|
||||
if (_released)
|
||||
return;
|
||||
|
||||
if ((DateTime.UtcNow - _lastSync).Seconds <= _options.ThrottleSeconds)
|
||||
if ((DateTime.UtcNow - _lastSync).TotalSeconds <= _options.ThrottleSeconds)
|
||||
return;
|
||||
|
||||
_syncing = true;
|
||||
@@ -213,9 +236,9 @@ namespace Umbraco.Core.Sync
|
||||
private void ProcessDatabaseInstructions()
|
||||
{
|
||||
// NOTE
|
||||
// we 'could' recurse to ensure that no remaining instructions are pending in the table before proceeding but I don't think that
|
||||
// we 'could' recurse to ensure that no remaining instructions are pending in the table before proceeding but I don't think that
|
||||
// would be a good idea since instructions could keep getting added and then all other threads will probably get stuck from serving requests
|
||||
// (depending on what the cache refreshers are doing). I think it's best we do the one time check, process them and continue, if there are
|
||||
// (depending on what the cache refreshers are doing). I think it's best we do the one time check, process them and continue, if there are
|
||||
// pending requests after being processed, they'll just be processed on the next poll.
|
||||
//
|
||||
// FIXME not true if we're running on a background thread, assuming we can?
|
||||
@@ -281,7 +304,7 @@ namespace Umbraco.Core.Sync
|
||||
/// Remove old instructions from the database
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always leave the last (most recent) record in the db table, this is so that not all instructions are removed which would cause
|
||||
/// Always leave the last (most recent) record in the db table, this is so that not all instructions are removed which would cause
|
||||
/// the site to cold boot if there's been no instruction activity for more than DaysToRetainInstructions.
|
||||
/// See: http://issues.umbraco.org/issue/U4-7643#comment=67-25085
|
||||
/// </remarks>
|
||||
@@ -290,15 +313,15 @@ namespace Umbraco.Core.Sync
|
||||
var pruneDate = DateTime.UtcNow.AddDays(-_options.DaysToRetainInstructions);
|
||||
var sqlSyntax = _appContext.DatabaseContext.SqlSyntax;
|
||||
|
||||
//NOTE: this query could work on SQL server and MySQL:
|
||||
//NOTE: this query could work on SQL server and MySQL:
|
||||
/*
|
||||
SELECT id
|
||||
FROM umbracoCacheInstruction
|
||||
WHERE utcStamp < getdate()
|
||||
WHERE utcStamp < getdate()
|
||||
AND id <> (SELECT MAX(id) FROM umbracoCacheInstruction)
|
||||
*/
|
||||
// However, this will not work on SQLCE and in fact it will be slower than the query we are
|
||||
// using if the SQL server doesn't perform it's own query optimizations (i.e. since the above
|
||||
// using if the SQL server doesn't perform it's own query optimizations (i.e. since the above
|
||||
// query could actually execute a sub query for every row found). So we've had to go with an
|
||||
// inner join which is faster and works on SQLCE but it's uglier to read.
|
||||
|
||||
@@ -331,9 +354,9 @@ namespace Umbraco.Core.Sync
|
||||
var dtos = _appContext.DatabaseContext.Database.Fetch<CacheInstructionDto>(sql);
|
||||
|
||||
if (dtos.Count == 0)
|
||||
_lastId = -1;
|
||||
_lastId = -1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the last-synced id from file into memory.
|
||||
/// </summary>
|
||||
@@ -502,4 +525,3 @@ namespace Umbraco.Core.Sync
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides options to the <see cref="DatabaseServerMessenger"/>.
|
||||
/// </summary>
|
||||
@@ -14,9 +14,15 @@ namespace Umbraco.Core.Sync
|
||||
public DatabaseServerMessengerOptions()
|
||||
{
|
||||
DaysToRetainInstructions = 2; // 2 days
|
||||
ThrottleSeconds = 5; // 5 seconds
|
||||
ThrottleSeconds = 5; // 5 second
|
||||
MaxProcessingInstructionCount = 1000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of instructions that can be processed at startup; otherwise the server cold-boots (rebuilds its caches).
|
||||
/// </summary>
|
||||
public int MaxProcessingInstructionCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of callbacks that will be invoked if the lastsynced.txt file does not exist.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
@@ -14,13 +15,18 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
StaleServerTimeout = TimeSpan.FromMinutes(2); // 2 minutes
|
||||
ThrottleSeconds = 30; // 30 seconds
|
||||
RecurringSeconds = 60; // do it every minute
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of seconds to wait between each updates to the database.
|
||||
/// </summary>
|
||||
[Obsolete("This is no longer used")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int ThrottleSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of seconds to wait between calls to the database on the background thread
|
||||
/// </summary>
|
||||
public int RecurringSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time span to wait before considering a server stale, after it has last been accessed.
|
||||
/// </summary>
|
||||
|
||||
@@ -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" />
|
||||
@@ -456,6 +458,7 @@
|
||||
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IAuditRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IContentTypeCompositionRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IDomainRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IExternalLoginRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IMigrationEntryRepository.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -13,9 +14,105 @@ 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;
|
||||
|
||||
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((string cacheKey, Func<object> o, TimeSpan? t, bool b, CacheItemPriority cip, CacheItemRemovedCallback circ, string[] s) =>
|
||||
{
|
||||
cached.Add(cacheKey);
|
||||
|
||||
list = o() as IList;
|
||||
});
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(() =>
|
||||
{
|
||||
//return null if this is the first pass
|
||||
return cached.Any() ? new DeepCloneableList<AuditItem>(ListCloneBehavior.CloneOnce) : null;
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
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, () => getAll, false);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, cached.Count);
|
||||
Assert.IsNotNull(list);
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
@@ -28,16 +125,12 @@ namespace Umbraco.Tests.Cache
|
||||
|
||||
list = o() as IList;
|
||||
});
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object);
|
||||
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);
|
||||
@@ -46,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);
|
||||
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());
|
||||
|
||||
@@ -55,10 +55,11 @@ namespace Umbraco.Tests.Persistence.Querying
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
IDictionary<int, IEnumerable<ContentTypeRepository.ContentTypeQueryMapper.AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<int, IEnumerable<int>> allParentContentTypeIds;
|
||||
IDictionary<int, List<ContentTypeRepository.ContentTypeQueryMapper.AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<int, List<int>> allParentContentTypeIds;
|
||||
var contentTypes = ContentTypeRepository.ContentTypeQueryMapper.MapContentTypes(
|
||||
new[] {99997, 99998}, DatabaseContext.Database, SqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
DatabaseContext.Database, SqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
.Where(x => (new[] {99997, 99998}).Contains(x.Id))
|
||||
.ToArray();
|
||||
|
||||
var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997);
|
||||
@@ -109,9 +110,10 @@ namespace Umbraco.Tests.Persistence.Querying
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
IDictionary<int, IEnumerable<int>> allParentContentTypeIds;
|
||||
IDictionary<int, List<int>> allParentContentTypeIds;
|
||||
var contentTypes = ContentTypeRepository.ContentTypeQueryMapper.MapMediaTypes(
|
||||
new[] { 99997, 99998 }, DatabaseContext.Database, SqlSyntax, out allParentContentTypeIds)
|
||||
DatabaseContext.Database, SqlSyntax, out allParentContentTypeIds)
|
||||
.Where(x => (new[] { 99997, 99998 }).Contains(x.Id))
|
||||
.ToArray();
|
||||
|
||||
var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
@@ -297,7 +326,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var allGuidIds = repository.GetAll().Select(x => x.Key).ToArray();
|
||||
|
||||
// Act
|
||||
var contentTypes = repository.GetAll(allGuidIds);
|
||||
var contentTypes = ((IReadRepository<Guid, IContentType>)repository).GetAll(allGuidIds);
|
||||
int count =
|
||||
DatabaseContext.Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM umbracoNode WHERE nodeObjectType = @NodeObjectType",
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
// Act
|
||||
|
||||
var mediaTypes = repository.GetAll(allGuidIds);
|
||||
var mediaTypes = ((IReadRepository<Guid, IMediaType>)repository).GetAll(allGuidIds);
|
||||
|
||||
int count =
|
||||
DatabaseContext.Database.ExecuteScalar<int>(
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
repository.AddOrUpdate(memberType2);
|
||||
unitOfWork.Commit();
|
||||
|
||||
var result = repository.GetAll(memberType1.Key, memberType2.Key);
|
||||
var result = ((IReadRepository<Guid, IMemberType>)repository).GetAll(memberType1.Key, memberType2.Key);
|
||||
|
||||
//there are 3 because of the Member type created for init data
|
||||
Assert.AreEqual(2, result.Count());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@
|
||||
<HintPath>..\packages\ImageProcessor.2.3.2.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.5.0.0\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.5.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.5.1.0\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -2414,9 +2414,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>7350</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>7360</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7350</IISUrl>
|
||||
<IISUrl>http://localhost:7370</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -6,12 +6,12 @@ Index sets can be defined in the ExamineIndex.config if you're using the standar
|
||||
|
||||
More information and documentation can be found on CodePlex: http://umbracoexamine.codeplex.com
|
||||
-->
|
||||
<Examine RebuildOnAppStart="false">
|
||||
<Examine>
|
||||
<ExamineIndexProviders>
|
||||
<providers>
|
||||
<add name="InternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
|
||||
supportUnpublished="true"
|
||||
supportProtected="true"
|
||||
supportProtected="true"
|
||||
analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
@@ -22,7 +22,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
<!-- default external indexer, which excludes protected and unpublished pages-->
|
||||
<add name="ExternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
|
||||
<add name="ExternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
</providers>
|
||||
@@ -31,15 +31,15 @@ More information and documentation can be found on CodePlex: http://umbracoexami
|
||||
<ExamineSearchProviders defaultProvider="ExternalSearcher">
|
||||
<providers>
|
||||
<add name="InternalSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
|
||||
analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"
|
||||
analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
<add name="ExternalSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
|
||||
<add name="ExternalSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
<add name="InternalMemberSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
|
||||
analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"
|
||||
enableLeadingWildcard="true"
|
||||
analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"
|
||||
enableLeadingWildcard="true"
|
||||
useTempStorage="Sync"/>
|
||||
|
||||
</providers>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.68.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.3.2.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.5.0.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.5.1.0" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
|
||||
@@ -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> ";
|
||||
}
|
||||
|
||||
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> ";
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<system.web>
|
||||
<customErrors mode="RemoteOnly" />
|
||||
<trace enabled="true" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
|
||||
<httpRuntime requestValidationMode="2.0" enableVersionHeader="false" targetFramework="4.5" maxRequestLength="1048576" fcnMode="Single" />
|
||||
<httpRuntime requestValidationMode="2.0" enableVersionHeader="false" targetFramework="4.5" maxRequestLength="51200" fcnMode="Single" />
|
||||
<!--
|
||||
If you are deploying to a cloud environment that has multiple web server instances,
|
||||
you should change session state mode from "InProc" to "Custom". In addition,
|
||||
@@ -229,7 +229,7 @@
|
||||
<!-- Increase the default upload file size limit -->
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<requestLimits maxAllowedContentLength="1073741824" />
|
||||
<requestLimits maxAllowedContentLength="52428800" />
|
||||
</requestFiltering>
|
||||
</security>
|
||||
|
||||
|
||||
@@ -128,7 +128,11 @@ namespace Umbraco.Web.Cache
|
||||
{
|
||||
ClearAllIsolatedCacheByEntityType<IContent>();
|
||||
ClearAllIsolatedCacheByEntityType<IContentType>();
|
||||
|
||||
ClearAllIsolatedCacheByEntityType<IMedia>();
|
||||
ClearAllIsolatedCacheByEntityType<IMediaType>();
|
||||
ClearAllIsolatedCacheByEntityType<IMember>();
|
||||
ClearAllIsolatedCacheByEntityType<IMemberType>();
|
||||
|
||||
//all property type cache
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
|
||||
//all content type property cache
|
||||
|
||||
@@ -27,6 +27,20 @@ namespace Umbraco.Web.Install.InstallSteps
|
||||
|
||||
public override InstallSetupResult Execute(object model)
|
||||
{
|
||||
var ih = new InstallHelper(UmbracoContext.Current);
|
||||
|
||||
//During a new install we'll log the default user in (which is id = 0).
|
||||
// During an upgrade, the user will already need to be logged in in order to run the installer.
|
||||
|
||||
var security = new WebSecurity(_httpContext, _applicationContext);
|
||||
//we do this check here because for upgrades the user will already be logged in, for brand new installs,
|
||||
// they will not be logged in, however we cannot check the current installation status because it will tell
|
||||
// us that it is in 'upgrade' because we already have a database conn configured and a database.
|
||||
if (security.IsAuthenticated() == false && GlobalSettings.ConfigurationStatus.IsNullOrWhiteSpace())
|
||||
{
|
||||
security.PerformLogin(0);
|
||||
}
|
||||
|
||||
//This is synonymous with library.RefreshContent() - but we don't want to use library
|
||||
// for anything anymore so welll use the method that it is wrapping. This will just make sure
|
||||
// the correct xml structure exists in the xml cache file. This is required by some upgrade scripts
|
||||
@@ -39,12 +53,8 @@ namespace Umbraco.Web.Install.InstallSteps
|
||||
// Update ClientDependency version
|
||||
var clientDependencyConfig = new ClientDependencyConfiguration(_applicationContext.ProfilingLogger.Logger);
|
||||
var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber();
|
||||
|
||||
var security = new WebSecurity(_httpContext, _applicationContext);
|
||||
security.PerformLogin(0);
|
||||
|
||||
//reports the ended install
|
||||
var ih = new InstallHelper(UmbracoContext.Current);
|
||||
|
||||
//reports the ended install
|
||||
ih.InstallStatus(true, "");
|
||||
|
||||
return null;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -166,7 +166,8 @@ namespace Umbraco.Web.Routing
|
||||
/// preserve or reset the template, if any.</remarks>
|
||||
public void SetInternalRedirectPublishedContent(IPublishedContent content)
|
||||
{
|
||||
EnsureWriteable();
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
EnsureWriteable();
|
||||
|
||||
// unless a template has been set already by the finder,
|
||||
// template should be null at that point.
|
||||
|
||||
@@ -515,11 +515,11 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
// redirect to another page
|
||||
var node = _routingContext.UmbracoContext.ContentCache.GetById(internalRedirectId);
|
||||
|
||||
_pcr.SetInternalRedirectPublishedContent(node); // don't use .PublishedContent here
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
redirect = true;
|
||||
_pcr.SetInternalRedirectPublishedContent(node); // don't use .PublishedContent here
|
||||
redirect = true;
|
||||
ProfilingLogger.Logger.Debug<PublishedContentRequestEngine>("{0}Redirecting to id={1}", () => tracePrefix, () => internalRedirectId);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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,4 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
@@ -6,6 +8,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Scheduling;
|
||||
|
||||
namespace Umbraco.Web.Strategies
|
||||
{
|
||||
@@ -22,74 +25,137 @@ namespace Umbraco.Web.Strategies
|
||||
/// </remarks>
|
||||
public sealed class ServerRegistrationEventHandler : ApplicationEventHandler
|
||||
{
|
||||
private readonly object _locko = new object();
|
||||
private DatabaseServerRegistrar _registrar;
|
||||
private DateTime _lastUpdated = DateTime.MinValue;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _backgroundTaskRunner;
|
||||
private bool _started = false;
|
||||
|
||||
// bind to events
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
{
|
||||
_registrar = ServerRegistrarResolver.Current.Registrar as DatabaseServerRegistrar;
|
||||
|
||||
_backgroundTaskRunner = new BackgroundTaskRunner<IBackgroundTask>(
|
||||
new BackgroundTaskRunnerOptions { AutoStart = true },
|
||||
applicationContext.ProfilingLogger.Logger);
|
||||
|
||||
// only for the DatabaseServerRegistrar
|
||||
if (_registrar == null) return;
|
||||
|
||||
//We will start the whole process when a successful request is made
|
||||
UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt;
|
||||
}
|
||||
|
||||
// handles route attempts.
|
||||
/// <summary>
|
||||
/// Handle when a request is made
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>
|
||||
/// We require this because:
|
||||
/// - ApplicationContext.UmbracoApplicationUrl is initialized by UmbracoModule in BeginRequest
|
||||
/// - RegisterServer is called on UmbracoModule.RouteAttempt which is triggered in ProcessRequest
|
||||
/// we are safe, UmbracoApplicationUrl has been initialized
|
||||
/// </remarks>
|
||||
private void UmbracoModuleRouteAttempt(object sender, RoutableAttemptEventArgs e)
|
||||
{
|
||||
if (e.HttpContext.Request == null || e.HttpContext.Request.Url == null) return;
|
||||
|
||||
switch (e.Outcome)
|
||||
{
|
||||
case EnsureRoutableOutcome.IsRoutable:
|
||||
// front-end request
|
||||
RegisterServer(e);
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
break;
|
||||
case EnsureRoutableOutcome.NotDocumentRequest:
|
||||
// anything else (back-end request, service...)
|
||||
//so it's not a document request, we'll check if it's a back office request
|
||||
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
|
||||
{
|
||||
RegisterServer(e);
|
||||
break;
|
||||
/*
|
||||
case EnsureRoutableOutcome.NotReady:
|
||||
case EnsureRoutableOutcome.NotConfigured:
|
||||
case EnsureRoutableOutcome.NoContent:
|
||||
default:
|
||||
// otherwise, do nothing
|
||||
break;
|
||||
*/
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// register current server (throttled).
|
||||
|
||||
private void RegisterServer(UmbracoRequestEventArgs e)
|
||||
{
|
||||
lock (_locko) // ensure we trigger only once
|
||||
{
|
||||
var secondsSinceLastUpdate = DateTime.Now.Subtract(_lastUpdated).TotalSeconds;
|
||||
if (secondsSinceLastUpdate < _registrar.Options.ThrottleSeconds) return;
|
||||
_lastUpdated = DateTime.Now;
|
||||
}
|
||||
//only process once
|
||||
if (_started) return;
|
||||
|
||||
_started = true;
|
||||
|
||||
var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl;
|
||||
var svc = e.UmbracoContext.Application.Services.ServerRegistrationService;
|
||||
|
||||
// because
|
||||
// - ApplicationContext.UmbracoApplicationUrl is initialized by UmbracoModule in BeginRequest
|
||||
// - RegisterServer is called on UmbracoModule.RouteAttempt which is triggered in ProcessRequest
|
||||
// we are safe, UmbracoApplicationUrl has been initialized
|
||||
var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl;
|
||||
//Perform the rest async, we don't want to block the startup sequence
|
||||
// this will just reoccur on a background thread
|
||||
_backgroundTaskRunner.Add(new TouchServerTask(_backgroundTaskRunner,
|
||||
15000, //delay before first execution
|
||||
_registrar.Options.RecurringSeconds * 1000, //amount of ms between executions
|
||||
svc, _registrar, serverAddress));
|
||||
}
|
||||
|
||||
try
|
||||
private class TouchServerTask : RecurringTaskBase
|
||||
{
|
||||
private readonly IServerRegistrationService _svc;
|
||||
private readonly DatabaseServerRegistrar _registrar;
|
||||
private readonly string _serverAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RecurringTaskBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="runner">The task runner.</param>
|
||||
/// <param name="delayMilliseconds">The delay.</param>
|
||||
/// <param name="periodMilliseconds">The period.</param>
|
||||
/// <param name="svc"></param>
|
||||
/// <param name="registrar"></param>
|
||||
/// <param name="serverAddress"></param>
|
||||
/// <remarks>The task will repeat itself periodically. Use this constructor to create a new task.</remarks>
|
||||
public TouchServerTask(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
IServerRegistrationService svc, DatabaseServerRegistrar registrar, string serverAddress)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
svc.TouchServer(serverAddress, svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout);
|
||||
if (svc == null) throw new ArgumentNullException("svc");
|
||||
_svc = svc;
|
||||
_registrar = registrar;
|
||||
_serverAddress = serverAddress;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
public override bool IsAsync
|
||||
{
|
||||
LogHelper.Error<ServerRegistrationEventHandler>("Failed to update server record in database.", ex);
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool RunsOnShutdown
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the background task.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether to repeat the task.</returns>
|
||||
public override bool PerformRun()
|
||||
{
|
||||
try
|
||||
{
|
||||
_svc.TouchServer(_serverAddress, _svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout);
|
||||
|
||||
return true; // repeat
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<ServerRegistrationEventHandler>("Failed to update server record in database.", ex);
|
||||
|
||||
return false; // probably stop if we have an error
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<bool> PerformRunAsync(CancellationToken token)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace Umbraco.Web
|
||||
if (umbracoSettings == null) throw new ArgumentNullException("umbracoSettings");
|
||||
if (urlProviders == null) throw new ArgumentNullException("urlProviders");
|
||||
|
||||
//if there's already a singleton, and we're not replacing then there's no need to ensure anything
|
||||
if (UmbracoContext.Current != null)
|
||||
{
|
||||
if (replaceContext == false)
|
||||
@@ -132,6 +133,39 @@ namespace Umbraco.Web
|
||||
UmbracoContext.Current._replacing = true;
|
||||
}
|
||||
|
||||
var umbracoContext = CreateContext(httpContext, applicationContext, webSecurity, umbracoSettings, urlProviders, preview ?? false);
|
||||
|
||||
//assign the singleton
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a standalone UmbracoContext instance
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <param name="webSecurity"></param>
|
||||
/// <param name="umbracoSettings"></param>
|
||||
/// <param name="urlProviders"></param>
|
||||
/// <param name="preview"></param>
|
||||
/// <returns>
|
||||
/// A new instance of UmbracoContext
|
||||
/// </returns>
|
||||
public static UmbracoContext CreateContext(
|
||||
HttpContextBase httpContext,
|
||||
ApplicationContext applicationContext,
|
||||
WebSecurity webSecurity,
|
||||
IUmbracoSettingsSection umbracoSettings,
|
||||
IEnumerable<IUrlProvider> urlProviders,
|
||||
bool preview)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
if (applicationContext == null) throw new ArgumentNullException("applicationContext");
|
||||
if (webSecurity == null) throw new ArgumentNullException("webSecurity");
|
||||
if (umbracoSettings == null) throw new ArgumentNullException("umbracoSettings");
|
||||
if (urlProviders == null) throw new ArgumentNullException("urlProviders");
|
||||
|
||||
var umbracoContext = new UmbracoContext(
|
||||
httpContext,
|
||||
applicationContext,
|
||||
@@ -142,15 +176,15 @@ namespace Umbraco.Web
|
||||
// create the RoutingContext, and assign
|
||||
var routingContext = new RoutingContext(
|
||||
umbracoContext,
|
||||
|
||||
|
||||
//TODO: Until the new cache is done we can't really expose these to override/mock
|
||||
new Lazy<IEnumerable<IContentFinder>>(() => ContentFinderResolver.Current.Finders),
|
||||
new Lazy<IContentFinder>(() => ContentLastChanceFinderResolver.Current.Finder),
|
||||
|
||||
|
||||
// create the nice urls provider
|
||||
// there's one per request because there are some behavior parameters that can be changed
|
||||
new Lazy<UrlProvider>(
|
||||
() => new UrlProvider(
|
||||
() => new UrlProvider(
|
||||
umbracoContext,
|
||||
umbracoSettings.WebRouting,
|
||||
urlProviders),
|
||||
@@ -159,9 +193,7 @@ namespace Umbraco.Web
|
||||
//assign the routing context back
|
||||
umbracoContext.RoutingContext = routingContext;
|
||||
|
||||
//assign the singleton
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
return UmbracoContext.Current;
|
||||
return umbracoContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -460,18 +492,9 @@ namespace Umbraco.Web
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
Security.DisposeIfDisposable();
|
||||
Security = null;
|
||||
_umbracoContext = null;
|
||||
//ensure not to dispose this!
|
||||
Application = null;
|
||||
|
||||
//Before we set these to null but in fact these are application lifespan singletons so
|
||||
//there's no reason we need to set them to null and this also caused a problem with packages
|
||||
//trying to access the cache properties on RequestEnd.
|
||||
//http://issues.umbraco.org/issue/U4-2734
|
||||
//http://our.umbraco.org/projects/developer-tools/301-url-tracker/version-2/44327-Issues-with-URL-Tracker-in-614
|
||||
//ContentCache = null;
|
||||
//MediaCache = null;
|
||||
//If not running in a web ctx, ensure the thread based instance is nulled
|
||||
_umbracoContext = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using System.Web.Routing;
|
||||
using ClientDependency.Core.Config;
|
||||
using Examine;
|
||||
using Examine.Config;
|
||||
using Examine.Providers;
|
||||
using umbraco;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -39,6 +40,7 @@ using Umbraco.Web.Scheduling;
|
||||
using Umbraco.Web.UI.JavaScript;
|
||||
using Umbraco.Web.WebApi;
|
||||
using umbraco.BusinessLogic;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
@@ -50,13 +52,13 @@ using ProfilingViewEngine = Umbraco.Core.Profiling.ProfilingViewEngine;
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// A bootstrapper for the Umbraco application which initializes all objects including the Web portion of the application
|
||||
/// A bootstrapper for the Umbraco application which initializes all objects including the Web portion of the application
|
||||
/// </summary>
|
||||
public class WebBootManager : CoreBootManager
|
||||
{
|
||||
private readonly bool _isForTesting;
|
||||
//NOTE: see the Initialize method for what this is used for
|
||||
private readonly List<IIndexer> _indexesToRebuild = new List<IIndexer>();
|
||||
private static readonly List<BaseIndexProvider> IndexesToRebuild = new List<BaseIndexProvider>();
|
||||
|
||||
public WebBootManager(UmbracoApplicationBase umbracoApplication)
|
||||
: base(umbracoApplication)
|
||||
@@ -103,7 +105,7 @@ namespace Umbraco.Web
|
||||
public override IBootManager Initialize()
|
||||
{
|
||||
//This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976
|
||||
// when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
|
||||
// when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
|
||||
// event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
|
||||
// it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
|
||||
// boot process has completed. It's a hack but it works.
|
||||
@@ -144,7 +146,7 @@ namespace Umbraco.Web
|
||||
{ "compositeFileHandlerPath", ClientDependencySettings.Instance.CompositeFileHandlerPath }
|
||||
});
|
||||
ClientDependencySettings.Instance.MvcRendererCollection.Add(renderer);
|
||||
|
||||
|
||||
// Disable the X-AspNetMvc-Version HTTP Header
|
||||
MvcHandler.DisableMvcResponseHeader = true;
|
||||
|
||||
@@ -153,9 +155,9 @@ namespace Umbraco.Web
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Override this method in order to ensure that the UmbracoContext is also created, this can only be
|
||||
/// Override this method in order to ensure that the UmbracoContext is also created, this can only be
|
||||
/// created after resolution is frozen!
|
||||
/// </summary>
|
||||
protected override void FreezeResolution()
|
||||
@@ -169,8 +171,8 @@ namespace Umbraco.Web
|
||||
httpContext,
|
||||
ApplicationContext,
|
||||
new WebSecurity(httpContext, ApplicationContext),
|
||||
UmbracoConfig.For.UmbracoSettings(),
|
||||
UrlProviderResolver.Current.Providers,
|
||||
UmbracoConfig.For.UmbracoSettings(),
|
||||
UrlProviderResolver.Current.Providers,
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -180,11 +182,10 @@ namespace Umbraco.Web
|
||||
protected override void InitializeProfilerResolver()
|
||||
{
|
||||
base.InitializeProfilerResolver();
|
||||
|
||||
//Set the profiler to be the web profiler
|
||||
ProfilerResolver.Current.SetProfiler(new WebProfiler());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the OnApplicationStarted methods of the IApplicationEvents are called
|
||||
/// </summary>
|
||||
@@ -202,14 +203,14 @@ namespace Umbraco.Web
|
||||
|
||||
//Now, startup all of our legacy startup handler
|
||||
ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers();
|
||||
|
||||
//Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them
|
||||
|
||||
//Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them
|
||||
// (see the initialize method for notes) - we'll ensure we remove the event handler too in case examine manager doesn't actually
|
||||
// initialize during startup, in which case we want it to rebuild the indexes itself.
|
||||
ExamineManager.Instance.BuildingEmptyIndexOnStartup -= OnInstanceOnBuildingEmptyIndexOnStartup;
|
||||
if (_indexesToRebuild.Any())
|
||||
if (IndexesToRebuild.Any())
|
||||
{
|
||||
foreach (var indexer in _indexesToRebuild)
|
||||
foreach (var indexer in IndexesToRebuild)
|
||||
{
|
||||
indexer.RebuildIndex();
|
||||
}
|
||||
@@ -240,7 +241,19 @@ namespace Umbraco.Web
|
||||
protected override CacheHelper CreateApplicationCache()
|
||||
{
|
||||
//create a web-based cache helper
|
||||
return new CacheHelper();
|
||||
var cacheHelper = new CacheHelper(
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)),
|
||||
new StaticCacheProvider(),
|
||||
//we have no request based cache when not running in web-based context
|
||||
new NullCacheProvider(),
|
||||
new IsolatedRuntimeCache(type =>
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
|
||||
|
||||
return cacheHelper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -267,7 +280,7 @@ namespace Umbraco.Web
|
||||
//plugin controllers must come first because the next route will catch many things
|
||||
RoutePluginControllers();
|
||||
}
|
||||
|
||||
|
||||
private void RoutePluginControllers()
|
||||
{
|
||||
var umbracoPath = GlobalSettings.UmbracoMvcArea;
|
||||
@@ -334,14 +347,14 @@ namespace Umbraco.Web
|
||||
umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}",//url to match
|
||||
new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
|
||||
new[] { meta.ControllerNamespace }); //look in this namespace to create the controller
|
||||
route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set
|
||||
route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set
|
||||
route.DataTokens.Add("UseNamespaceFallback", false); //Don't look anywhere else except this namespace!
|
||||
//make it use our custom/special SurfaceMvcHandler
|
||||
route.RouteHandler = new SurfaceRouteHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes all web based and core resolves
|
||||
/// Initializes all web based and core resolves
|
||||
/// </summary>
|
||||
protected override void InitializeResolvers()
|
||||
{
|
||||
@@ -360,7 +373,7 @@ namespace Umbraco.Web
|
||||
//set the legacy one by default - this maintains backwards compat
|
||||
ServerMessengerResolver.Current.SetServerMessenger(new BatchedWebServiceServerMessenger(() =>
|
||||
{
|
||||
//we should not proceed to change this if the app/database is not configured since there will
|
||||
//we should not proceed to change this if the app/database is not configured since there will
|
||||
// be no user, plus we don't need to have server messages sent if this is the case.
|
||||
if (ApplicationContext.IsConfigured && ApplicationContext.DatabaseContext.IsDatabaseConfigured)
|
||||
{
|
||||
@@ -388,49 +401,40 @@ namespace Umbraco.Web
|
||||
else
|
||||
{
|
||||
|
||||
// NOTE: This is IMPORTANT! ... we don't want to rebuild any index that is already flagged to be re-indexed
|
||||
// on startup based on our _indexesToRebuild variable and how Examine auto-rebuilds when indexes are empty
|
||||
// this callback is used below for the DatabaseServerMessenger startup options
|
||||
//We are using a custom action here so we can check the examine settings value first, we don't want to
|
||||
// put that check into the CreateIndexesOnColdBoot method because developers may choose to use this
|
||||
// method directly and they will be in charge of this check if they need it
|
||||
Action rebuildIndexes = () =>
|
||||
{
|
||||
//If the developer has explicitly opted out of rebuilding indexes on startup then we
|
||||
//If the developer has explicitly opted out of rebuilding indexes on startup then we
|
||||
// should adhere to that and not do it, this means that if they are load balancing things will be
|
||||
// out of sync if they are auto-scaling but there's not much we can do about that.
|
||||
if (ExamineSettings.Instance.RebuildOnAppStart == false) return;
|
||||
|
||||
if (_indexesToRebuild.Any())
|
||||
foreach (var indexer in GetIndexesForColdBoot())
|
||||
{
|
||||
var otherIndexes = ExamineManager.Instance.IndexProviderCollection.Except(_indexesToRebuild);
|
||||
foreach (var otherIndex in otherIndexes)
|
||||
{
|
||||
otherIndex.RebuildIndex();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//rebuild them all
|
||||
ExamineManager.Instance.RebuildIndex();
|
||||
indexer.RebuildIndex();
|
||||
}
|
||||
};
|
||||
|
||||
ServerMessengerResolver.Current.SetServerMessenger(new BatchedDatabaseServerMessenger(
|
||||
ApplicationContext,
|
||||
true,
|
||||
//Default options for web including the required callbacks to build caches
|
||||
new DatabaseServerMessengerOptions
|
||||
{
|
||||
//These callbacks will be executed if the server has not been synced
|
||||
// (i.e. it is a new server or the lastsynced.txt file has been removed)
|
||||
InitializingCallbacks = new Action[]
|
||||
ApplicationContext,
|
||||
true,
|
||||
//Default options for web including the required callbacks to build caches
|
||||
new DatabaseServerMessengerOptions
|
||||
{
|
||||
//rebuild the xml cache file if the server is not synced
|
||||
() => global::umbraco.content.Instance.RefreshContentFromDatabase(),
|
||||
//rebuild indexes if the server is not synced
|
||||
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
|
||||
// indexes then they can adjust this logic themselves.
|
||||
rebuildIndexes
|
||||
}
|
||||
}));
|
||||
//These callbacks will be executed if the server has not been synced
|
||||
// (i.e. it is a new server or the lastsynced.txt file has been removed)
|
||||
InitializingCallbacks = new Action[]
|
||||
{
|
||||
//rebuild the xml cache file if the server is not synced
|
||||
() => global::umbraco.content.Instance.RefreshContentFromDatabase(),
|
||||
//rebuild indexes if the server is not synced
|
||||
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
|
||||
// indexes then they can adjust this logic themselves.
|
||||
rebuildIndexes
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
SurfaceControllerResolver.Current = new SurfaceControllerResolver(
|
||||
@@ -455,7 +459,7 @@ namespace Umbraco.Web
|
||||
new PublishedCache.XmlPublishedCache.PublishedContentCache(),
|
||||
new PublishedCache.XmlPublishedCache.PublishedMediaCache(ApplicationContext)));
|
||||
|
||||
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
|
||||
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
|
||||
new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));
|
||||
|
||||
FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver(
|
||||
@@ -518,12 +522,42 @@ namespace Umbraco.Web
|
||||
new DefaultCultureDictionaryFactory());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method used to create indexes on a cold boot
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A cold boot is when the server determines it will not (or cannot) process instructions in the cache table and
|
||||
/// will rebuild it's own caches itself.
|
||||
/// </remarks>
|
||||
public static IEnumerable<BaseIndexProvider> GetIndexesForColdBoot()
|
||||
{
|
||||
// NOTE: This is IMPORTANT! ... we don't want to rebuild any index that is already flagged to be re-indexed
|
||||
// on startup based on our _indexesToRebuild variable and how Examine auto-rebuilds when indexes are empty.
|
||||
// This callback is used above for the DatabaseServerMessenger startup options.
|
||||
|
||||
// all indexes
|
||||
IEnumerable<BaseIndexProvider> indexes = ExamineManager.Instance.IndexProviderCollection;
|
||||
|
||||
// except those that are already flagged
|
||||
// and are processed in Complete()
|
||||
if (IndexesToRebuild.Any())
|
||||
indexes = indexes.Except(IndexesToRebuild);
|
||||
|
||||
// return
|
||||
foreach (var index in indexes)
|
||||
yield return index;
|
||||
|
||||
// and clear
|
||||
IndexesToRebuild.Clear();
|
||||
}
|
||||
|
||||
|
||||
private void OnInstanceOnBuildingEmptyIndexOnStartup(object sender, BuildingEmptyIndexOnStartupEventArgs args)
|
||||
{
|
||||
//store the indexer that needs rebuilding because it's empty for when the boot process
|
||||
//store the indexer that needs rebuilding because it's empty for when the boot process
|
||||
// is complete and cancel this current event so the rebuild process doesn't start right now.
|
||||
args.Cancel = true;
|
||||
_indexesToRebuild.Add(args.Indexer);
|
||||
IndexesToRebuild.Add((BaseIndexProvider)args.Indexer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -809,7 +809,7 @@ order by level,sortOrder";
|
||||
if (Level == 1) throw new ArgumentException("No newParent node");
|
||||
if (_parent == null)
|
||||
{
|
||||
_parent = ApplicationContext.Current.Services.EntityService.Get(Entity.ParentId);
|
||||
_parent = ApplicationContext.Current.Services.EntityService.Get(_parentid);
|
||||
}
|
||||
return new CMSNode(_parent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user