Compare commits

...

58 Commits

Author SHA1 Message Date
Shannon 856c345905 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 18:01:56 +01:00
Shannon b2cd5dfb85 adds null check to session id - might not exist in old cookies 2016-02-02 18:01:36 +01:00
Sebastiaan Janssen 462f7a59b5 Merge pull request #1078 from dawoe/U4-7872
Use product name in migration runner instead of Umbraco productname
2016-02-02 17:06:56 +01:00
Shannon 22689d24ec changes port 2016-02-02 17:05:21 +01:00
Shannon 4055afdf05 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 16:52:08 +01:00
Shannon a1c70a219a ensures correct sort order for content type queries + test 2016-02-02 16:51:57 +01:00
Sebastiaan Janssen 3151515855 U4-7072 Grid TinyMce with style_formats throws "Cannot assign to read only property 'name' 2016-02-02 16:09:02 +01:00
Shannon 25df9da715 bumps Version 2016-02-02 16:00:28 +01:00
Shannon 9c2f438476 fixes tests 2016-02-02 15:43:44 +01:00
Shannon 0de3be42cf U4-7869 Examine & SQL Azure - Timeouts - ensure the other reader is executed with retry. 2016-02-02 15:33:22 +01:00
Shannon 1545e0eed7 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 15:14:56 +01:00
Shannon 42a7ed6877 U4-7821 KeepUserLoggedIn with a long umbracoTimeOutInMinutes has logout issues 2016-02-02 15:14:47 +01:00
Sebastiaan Janssen 98b8aedc4d U4-7849 Unclear error message when applying public access 2016-02-02 15:13:57 +01:00
Shannon c7a2b54d1c Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 12:13:03 +01:00
Shannon 6583ff4439 U4-7821 KeepUserLoggedIn with a long umbracoTimeOutInMinutes has logout issues 2016-02-02 12:12:51 +01:00
Sebastiaan Janssen 7b08b3e936 Merged branch 7.3.7 into 7.3.7 2016-02-02 11:55:40 +01:00
Sebastiaan Janssen bd2fc71dc5 U4-7276 When creating folders in the "Partials" view folder a YSOD appears (Umbraco 7.3 and 7.4) 2016-02-02 11:55:24 +01:00
Shannon 0af97f63e2 U4-7857 Flexible Load Balancing does not sync with the correct timeout threshold 2016-02-02 11:11:47 +01:00
Shannon 1db635f24c Updates DeepCloneableList to support behaviors, for the FullDataSetCachePolicy we only want to clone when writing to cache, not when reading, the cloning will then be done on individual items after filtering by the FullDataSetRepositoryCachePolicy 2016-02-02 01:32:36 +01:00
Shannon 6e27b3d6d4 Fixes up the FullDataSetRepositoryCachePolicy to handle individual items, updates and removes correctly, splits up it's logic so it's not overriding the DefaultRepositoryCachePolicy since that is just different. Adds tests. 2016-02-02 00:47:18 +01:00
Shannon 1dea0edcf1 re-includes the static cache for published property types, this cache is much more than a simple cache of content type infos, it is also the cache for associated converters which is required for all front-end rendering. Fixes the issue of not setting the xpath cache level corectly. 2016-02-01 22:50:38 +01:00
Shannon 1abab41955 Fixes perf issue with DeepCloneHelper - so we cache the actual property types instead of re-reflecting each time 2016-02-01 21:45:34 +01:00
Shannon d1be38be64 reverts profiler changes, we'll inlcude in 7.4 2016-01-28 18:37:56 +01:00
Shannon 8916c68a0c reverts profiler changes, we'll inlcude in 7.4 2016-01-28 18:35:29 +01:00
Shannon 6faa7e2fc9 dont' have RebuildOnAppStart set for dev purposes! 2016-01-28 18:31:47 +01:00
Shannon a6093f41bd Fixes installer login for user and adds notes 2016-01-28 18:02:22 +01:00
Stephan 107e66e4da Merge pull request #1071 from umbraco/temp-U4-7682
U4-7682 Add option to DatabaseServerMessengerOptions to force a Cold …
2016-01-28 14:33:02 +01:00
Shannon 256849f8a6 adds nicer profiling during startup for app event handlers. 2016-01-28 14:19:38 +01:00
Stephan c9c451be65 U4-7682 Minor review adjustments 2016-01-28 14:19:32 +01:00
Shannon 1fcea083e7 Ensures no profiling occurs when not in debug mode 2016-01-28 14:10:13 +01:00
Shannon 85d6ee5713 Merge branch '7.3.6' of https://github.com/umbraco/Umbraco-CMS into 7.3.6 2016-01-28 13:50:55 +01:00
Shannon ec81583741 Fixes case sensitive matching on alias in TemplateRepository 2016-01-28 13:50:45 +01:00
Sebastiaan Janssen 332da9bf2d Merge pull request #1070 from umbraco/temp-RepoCachePerf736
Temp repo cache perf736
2016-01-28 12:15:24 +01:00
Shannon a0036d925e U4-7682 Add option to DatabaseServerMessengerOptions to force a Cold Boot if there are too many instructions 2016-01-28 12:14:30 +01:00
Shannon 81a99d6f7d adds null check and ensures that SetInternalRedirectPublishedContent cannot be called with a null result 2016-01-28 10:19:51 +01:00
Shannon e912238574 Saw there was another place with the incorrect case for the column name AllowedId 2016-01-28 09:32:01 +01:00
Shannon 9a830e5de6 fixes tests 2016-01-27 19:44:36 +01:00
Shannon 7d9ba0e36e manually backports changes from 7.4 fixes 2016-01-27 19:42:06 +01:00
Shannon 6c5e09fd84 Ensures that the main content type repositories are returning the correct result from their GetAll methods. Fixes the FullDataSetRepositoryCachePolicy to ensure that any request with Ids actually returns the result of GetAll and then filters to ensure caching is correct. 2016-01-27 19:26:08 +01:00
Sebastiaan Janssen 2f8c43c689 Merge pull request #1057 from umbraco/temp-U4-7836
U4-7836 YSOD when upgrading Umbraco with error during AddUserDataClaims
2016-01-26 19:25:04 +01:00
Shannon 7a25cb52b2 updates the repository cache policies so that we aren't keeping everything in cache unnecessarily 2016-01-26 19:13:42 +01:00
Shannon 19bc97a7fd Removes the static (hack) cache for content types for creating published content 2016-01-26 18:56:29 +01:00
Shannon 72900ef3b3 U4-7813 Enable MiniProfiler to profiler during startup
Conflicts:
	src/Umbraco.Core/Umbraco.Core.csproj
2016-01-26 17:56:25 +01:00
Shannon 74ddd09c5d U4-7836 YSOD when upgrading Umbraco with error during AddUserDataClaims - this also ensures that no empty/null sections can be added to the user object since this is where the original exception was coming from. 2016-01-26 17:05:46 +01:00
Sebastiaan Janssen e461cb92da U4-7787 Shorthand use of ?altTemplate= doesn't work
#U4-7787 Fixed
2016-01-26 16:07:44 +01:00
Mads Rasmussen e91b53e66d back ported: https://github.com/Umbraco/Umbraco-CMS/commit/2d0f198f58071f5113d5c3437f10f2b85220c348 2016-01-25 11:09:20 +01:00
Shannon b5e8cb29dd ensure appctx and security isn't nulled on disposal (it's an application singleton it doesn't need to be nulled), creates new CreateContext method for creating standalone UmbracoContext instances. 2016-01-23 12:38:08 +01:00
Shannon 20dc4f5bc4 fixes merge issue/build 2016-01-23 12:27:40 +01:00
Shannon 2c399860ca Fixes DisposableTimer so that the profiled step is disposed - and thus tracked/timed properly! 2016-01-22 17:27:46 +01:00
Shannon 4dc4c36581 U4-7811 Content type repositories should use FullDataSetRepositoryCachePolicyFactory for performance
Conflicts:
	src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
	src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs
	src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs
	src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs
2016-01-22 17:08:21 +01:00
Shannon fe774f557c Fixes ms value: U4-7810 DatabaseServerRegistrar should not execute as soon as a request is made, this slows down app start 2016-01-22 17:06:14 +01:00
Shannon 3ecb9d0f86 U4-7807 Domain, language, public access cache (GetAll) caches not working when there are no items 2016-01-22 17:05:27 +01:00
Shannon f78a425985 U4-7816 The IsolatedCache instance in the RepositoryFactory differs from the ApplicationContext singleton instance 2016-01-22 16:37:47 +01:00
Sebastiaan Janssen 9f365727e0 Bumps version 2016-01-20 14:10:31 +01:00
Sebastiaan Janssen 74d73cfe94 U4-7796 Get UrlRewritingNet from the official NuGet location
#U4-7796 Fixed
2016-01-20 13:57:35 +01:00
Sebastiaan Janssen 7fb9d430a9 changes default upload limit to 50 mb 2016-01-20 10:54:43 +01:00
Sebastiaan Janssen 7cd8ba13c0 U4-7781 Setting a user's start node causes YSOD
#U4-7781 Fixed
2016-01-20 10:45:40 +01:00
Sebastiaan Janssen 1888888650 Update ImageProcessor, this time also in the nuspec file 2016-01-19 11:07:17 +01:00
76 changed files with 1941 additions and 1033 deletions
+3 -3
View File
@@ -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 -->
+2 -2
View File
@@ -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 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.3.5
7.3.7
+2 -2
View File
@@ -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
}
}
}
+29 -45
View File
@@ -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.
+12 -3
View File
@@ -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)
{
+44 -21
View File
@@ -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);
}
}
}
+6
View File
@@ -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
+99 -61
View File
@@ -9,10 +9,30 @@ namespace Umbraco.Core.Models
{
public static class DeepCloneHelper
{
/// <summary>
/// Stores the metadata for the properties for a given type so we know how to create them
/// </summary>
private struct ClonePropertyInfo
{
public ClonePropertyInfo(PropertyInfo propertyInfo) : this()
{
if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");
PropertyInfo = propertyInfo;
}
public PropertyInfo PropertyInfo { get; private set; }
public bool IsDeepCloneable { get; set; }
public Type GenericListType { get; set; }
public bool IsList
{
get { return GenericListType != null; }
}
}
/// <summary>
/// Used to avoid constant reflection (perf)
/// </summary>
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
private static readonly ConcurrentDictionary<Type, ClonePropertyInfo[]> PropCache = new ConcurrentDictionary<Type, ClonePropertyInfo[]>();
/// <summary>
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
@@ -30,81 +50,99 @@ namespace Umbraco.Core.Models
throw new InvalidOperationException("Both the input and output types must be the same");
}
//get the property metadata from cache so we only have to figure this out once per type
var refProperties = PropCache.GetOrAdd(inputType, type =>
inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
.Select<PropertyInfo, ClonePropertyInfo?>(propertyInfo =>
{
if (
//is not attributed with the ignore clone attribute
propertyInfo.GetCustomAttribute<DoNotCloneAttribute>() != null
//reference type but not string
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|| propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof (string)
//settable
&& x.CanWrite
|| propertyInfo.CanWrite == false
//non-indexed
&& x.GetIndexParameters().Any() == false)
|| propertyInfo.GetIndexParameters().Any())
{
return null;
}
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
{
return new ClonePropertyInfo(propertyInfo) { IsDeepCloneable = true };
}
if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
{
if (propertyInfo.PropertyType.IsGenericType
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
return new ClonePropertyInfo(propertyInfo) { GenericListType = genericType };
}
if (propertyInfo.PropertyType.IsArray
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
{
//if its an array, we'll create a list to work with first and then convert to array later
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
return new ClonePropertyInfo(propertyInfo) { GenericListType = typeof(List<object>) };
}
//skip instead of trying to create instance of abstract or interface
if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
{
return null;
}
//its a custom IEnumerable, we'll try to create it
try
{
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
//if it's an IList we can work with it, otherwise we cannot
var newList = custom as IList;
if (newList == null)
{
return null;
}
return new ClonePropertyInfo(propertyInfo) {GenericListType = propertyInfo.PropertyType};
}
catch (Exception)
{
//could not create this type so we'll skip it
return null;
}
}
return new ClonePropertyInfo(propertyInfo);
})
.Where(x => x.HasValue)
.Select(x => x.Value)
.ToArray());
foreach (var propertyInfo in refProperties)
foreach (var clonePropertyInfo in refProperties)
{
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
if (clonePropertyInfo.IsDeepCloneable)
{
//this ref property is also deep cloneable so clone it
var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
var result = (IDeepCloneable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (result != null)
{
//set the cloned value to the property
propertyInfo.SetValue(output, result.DeepClone(), null);
clonePropertyInfo.PropertyInfo.SetValue(output, result.DeepClone(), null);
}
}
else if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
else if (clonePropertyInfo.IsList)
{
IList newList;
if (propertyInfo.PropertyType.IsGenericType
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
newList = (IList)Activator.CreateInstance(genericType);
}
else if (propertyInfo.PropertyType.IsArray
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
{
//if its an array, we'll create a list to work with first and then convert to array later
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
newList = new List<object>();
}
else
{
//skip instead of trying to create instance of abstract or interface
if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
{
continue;
}
//its a custom IEnumerable, we'll try to create it
try
{
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
//if it's an IList we can work with it, otherwise we cannot
newList = custom as IList;
if (newList == null)
{
continue;
}
}
catch (Exception)
{
//could not create this type so we'll skip it
continue;
}
}
var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
var enumerable = (IEnumerable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (enumerable == null) continue;
var newList = (IList)Activator.CreateInstance(clonePropertyInfo.GenericListType);
var isUsableType = true;
//now clone each item
@@ -136,21 +174,21 @@ namespace Umbraco.Core.Models
continue;
}
if (propertyInfo.PropertyType.IsArray)
if (clonePropertyInfo.PropertyInfo.PropertyType.IsArray)
{
//need to convert to array
var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
var arr = (object[])Activator.CreateInstance(clonePropertyInfo.PropertyInfo.PropertyType, newList.Count);
for (int i = 0; i < newList.Count; i++)
{
arr[i] = newList[i];
}
//set the cloned collection
propertyInfo.SetValue(output, arr, null);
clonePropertyInfo.PropertyInfo.SetValue(output, arr, null);
}
else
{
//set the cloned collection
propertyInfo.SetValue(output, newList, null);
clonePropertyInfo.PropertyInfo.SetValue(output, newList, null);
}
}
@@ -230,13 +230,13 @@ namespace Umbraco.Core.Models.PublishedContent
{
_sourceCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Source);
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Object);
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
_xpathCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
}
else
{
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
_xpathCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
}
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
@@ -289,7 +289,7 @@ namespace Umbraco.Core.Persistence.Migrations
//NOTE: We CANNOT do this as part of the transaction!!! This is because when upgrading to 7.3, we cannot
// create the migrations table and then add data to it in the same transaction without issuing things like GO
// commands and since we need to support all Dbs, we need to just do this after the fact.
var exists = _migrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, _targetVersion);
var exists = _migrationEntryService.FindEntry(_productName, _targetVersion);
if (exists == null)
{
_migrationEntryService.CreateEntry(_productName, _targetVersion);
+1 -1
View File
@@ -834,7 +834,7 @@ namespace Umbraco.Core.Persistence
var pd = PocoData.ForType(typeof(T));
try
{
r = cmd.ExecuteReader();
r = cmd.ExecuteReaderWithRetry();
OnExecutedCommand(cmd);
}
catch (Exception x)
@@ -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));
}
}
@@ -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)
{
+2 -5
View File
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Security
/// Use this constructor to create/assign new UserData to the ticket
/// </summary>
/// <param name="sessionId">
/// A unique id that is assigned to this ticket
/// The security stamp for the user
/// </param>
public UserData(string sessionId)
{
@@ -30,8 +30,7 @@ namespace Umbraco.Core.Security
}
/// <summary>
/// This is used to Id the current ticket which we can then use to mitigate csrf attacks
/// and other things that require request validation.
/// This is the 'security stamp' for validation
/// </summary>
[DataMember(Name = "sessionId")]
public string SessionId { get; set; }
@@ -42,8 +41,6 @@ namespace Umbraco.Core.Security
[DataMember(Name = "roles")]
public string[] Roles { get; set; }
//public int SessionTimeout { get; set; }
[DataMember(Name = "username")]
public string Username { get; set; }
@@ -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>
+3
View File
@@ -175,6 +175,7 @@
<Compile Include="Cache\IJsonCacheRefresher.cs" />
<Compile Include="Cache\JsonCacheRefresherBase.cs" />
<Compile Include="Cache\NullCacheProvider.cs" />
<Compile Include="Cache\RepositoryCachePolicyBase.cs" />
<Compile Include="Cache\SingleItemsOnlyRepositoryCachePolicy.cs" />
<Compile Include="Cache\OnlySingleItemsRepositoryCachePolicyFactory.cs" />
<Compile Include="Cache\PayloadCacheRefresherBase.cs" />
@@ -187,6 +188,7 @@
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
<Compile Include="Collections\DeepCloneableList.cs" />
<Compile Include="Collections\ListCloneBehavior.cs" />
<Compile Include="ConcurrentHashSet.cs" />
<Compile Include="Configuration\BaseRest\IBaseRestSection.cs" />
<Compile Include="Configuration\BaseRest\IExtension.cs" />
@@ -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);
}
+92 -1
View File
@@ -67,4 +67,95 @@ iframe, .content-column-body {
}
.icon-chevron-down:before {
content: "\e0c9";
}
}
/* Styling for validation in Public Access */
.pa-umb-overlay {
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.pa-umb-overlay + .pa-umb-overlay {
padding-top: 30px;
border-top: 1px solid @grayLight;
}
.pa-select-type {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
align-items: flex-start;
margin-top: 15px;
}
.pa-select-type label {
padding: 0 20px;
}
.pa-access-header {
font-weight: bold;
margin: 0 0 3px 0;
padding-bottom: 0;
}
.pa-access-description {
color: #b3b3b3;
margin: 0;
}
.pa-validation-message {
padding: 6px 12px !important;
margin: 5px 0 0 0 !important;
display: inline-block;
}
.pa-select-pages label {
margin: 0;
font-size: 15px;
}
.pa-select-pages label + .controls-row {
padding-top: 0;
}
.pa-select-pages .umb-detail {
font-size: 13px;
margin: 2px 0 5px;
}
.pa-choose-page a {
color: @blue;
font-size: 15px;
}
.pa-choose-page a:hover, .pa-choose-page a:active, .pa-choose-page a:focus {
color: @blueDark;
text-decoration: none;
}
.pa-choose-page a:before {
content:"+";
margin-right: 3px;
font-weight: bold;
}
.pa-choose-page .treePickerTitle {
font-weight: bold;
font-size: 13px;
font-style: italic;
background: whitesmoke;
padding: 3px 5px;
color: grey;
border-bottom: none;
}
.pa-form + .pa-form {
margin-top: 10px;
}
+4 -4
View File
@@ -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>
+1 -1
View File
@@ -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> &nbsp;";
}
function updateErrorId() {
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
if (treePicker != undefined) {
document.getElementById("errorId").value = treePicker;
if (treePicker > 0) {
umbraco.presentation.webservices.CMSNode.GetNodeName('<%=umbraco.BasePages.BasePage.umbracoUserContextID%>', treePicker, updateErrorTitle);
} else
} else
document.getElementById("errorTitle").innerHTML = "<strong><%=umbraco.ui.Text("content", base.getUser())%></strong>";
}
}
}
function updateErrorTitle(result) {
document.getElementById("errorTitle").innerHTML = "<strong>" + result + "</strong> &nbsp;";
}
@@ -49,7 +49,7 @@
document.getElementById("pagesForm").style.display = "none";
}
}
function togglePages() {
document.getElementById("pagesForm").style.display = "block";
}
@@ -81,34 +81,37 @@
<cc1:Feedback ID="feedback" runat="server" />
<asp:Panel ID="p_mode" runat="server">
<asp:Panel ID="p_mode" runat="server" CssClass="pa-umb-overlay">
<div class="umg-dialog-body">
<cc1:Pane ID="pane_chooseMode" runat="server" Text="Choose how to restict access to this page">
<asp:RadioButton GroupName="mode" ID="rb_simple" runat="server" Style="float: left; margin: 10px;" Checked="true" />
<div class="pa-select-type">
<asp:RadioButton GroupName="mode" ID="rb_simple" runat="server" Checked="true" />
<div style="float: right;">
<h4 style="padding-top: 0px;"><%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%></h4>
<p><%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%></p>
</div>
<label for="body_rb_simple">
<h4 class="pa-access-header"><%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%></h4>
<p class="pa-access-description"><%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%></p>
</label>
</div>
<br style="clear: both;" />
<asp:RadioButton GroupName="mode" ID="rb_advanced" runat="server" Style="float: left; margin: 10px;" />
<div class="pa-select-type">
<asp:RadioButton GroupName="mode" ID="rb_advanced" runat="server"/>
<div style="float: left; padding-left: 10px;">
<h4 style="padding-top: 0px;"><%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%></h4>
<p><%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%></p>
<label for="body_rb_advanced">
<h4 class="pa-access-header"><%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%></h4>
<p class="pa-access-description"><%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%></p>
<asp:Panel runat="server" Visible="false" ID="p_noGroupsFound" CssClass="error">
<p>
<%= umbraco.ui.Text("publicAccess", "paAdvancedNoGroups", UmbracoUser)%>
</p>
</asp:Panel>
</label>
</div>
</div>
</cc1:Pane>
</div>
@@ -119,20 +122,24 @@
</asp:Panel>
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Single user protection">
<cc1:PropertyPanel ID="PropertyPanel1" runat="server">
<p><%= umbraco.ui.Text("publicAccess", "paSetLogin", UmbracoUser)%></p>
<asp:CustomValidator runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:PropertyPanel>
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="150px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="150px"></asp:TextBox>
</cc1:PropertyPanel>
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Set the login and password for this page" CssClass="pa-umb-overlay">
<div class="pa-form">
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="250px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
</div>
<div class="pa-form">
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="250px"></asp:TextBox>
</cc1:PropertyPanel>
</div>
<asp:CustomValidator CssClass="pa-validation-message error" runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:Pane>
<cc1:Pane ID="pane_advanced" runat="server" Visible="false" Text="Role based protection">
@@ -144,27 +151,29 @@
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="p_buttons" runat="server" Visible="false">
<asp:Panel ID="p_buttons" runat="server" Visible="false" CssClass="pa-umb-overlay">
<cc1:Pane runat="server" ID="pane_pages" Text="Select the pages that contain login form and error messages">
<cc1:PropertyPanel runat="server" ID="pp_loginPage">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
<asp:CustomValidator ErrorMessage="*" runat="server" ID="cv_loginPage" ForeColor="Red" />
<br />
<small>
<cc1:PropertyPanel runat="server" ID="pp_loginPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=umbraco.ui.Text("paLoginPageHelp")%>
</small>
<br />
<br />
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick a login page" runat="server" ID="cv_loginPage" ForeColor="Red" />
</cc1:PropertyPanel>
<cc1:PropertyPanel runat="server" ID="pp_errorPage">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
<asp:CustomValidator ErrorMessage="*" runat="server" ID="cv_errorPage" ForeColor="Red" />
<br />
<small>
<cc1:PropertyPanel runat="server" ID="pp_errorPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=umbraco.ui.Text("paErrorPageHelp")%>
</small>
<br />
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick an error page" runat="server" ID="cv_errorPage" ForeColor="Red" />
</cc1:PropertyPanel>
</cc1:Pane>
+2 -2
View File
@@ -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();
}
}
}
+40 -17
View File
@@ -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;
}
}
}
+87 -53
View File
@@ -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;
}
+1 -1
View File
@@ -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);
}