Compare commits
155 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39b1357a2a | |||
| e1ee8a1437 | |||
| 21ba88a34d | |||
| b5abbc7c30 | |||
| 7958b5fcb1 | |||
| 44b22503e1 | |||
| bfa12856db | |||
| d9542f4ca5 | |||
| f057976f78 | |||
| 151c28a20f | |||
| d2c8268fc4 | |||
| 13a9f40815 | |||
| 67f8141015 | |||
| 06a098215b | |||
| 051b320904 | |||
| 5aab50ba3b | |||
| 5b9cbae860 | |||
| 59759c4919 | |||
| 68ddea2776 | |||
| 8857b2db59 | |||
| fae5618865 | |||
| 40dbcbd7d9 | |||
| 074d894271 | |||
| eb6ae95039 | |||
| 5e461b0cec | |||
| d6a49f275f | |||
| 8e024fccff | |||
| 43227d4510 | |||
| 79cc4cd2fa | |||
| ff83d2e1f4 | |||
| cb7df58fdd | |||
| 5cc274a580 | |||
| 6c5b5ee637 | |||
| 9bfac0fdcd | |||
| bd5823f0de | |||
| 70035632b6 | |||
| 20f5560767 | |||
| ecd024c462 | |||
| ba91bf0b9c | |||
| 2b13297c36 | |||
| fb5faa3db7 | |||
| d7a142e724 | |||
| 63785ae70f | |||
| 68aa6ff093 | |||
| 3e985acef1 | |||
| 6d13d33cfd | |||
| ced0a852b1 | |||
| ba0da15f7f | |||
| 2875cd10f3 | |||
| b91661cf65 | |||
| 827f7a7bc8 | |||
| ff829d49a3 | |||
| d2483159bc | |||
| 48ab3f580c | |||
| c524d67a37 | |||
| 4f40fff5ee | |||
| b04d0fc886 | |||
| 4c3258812c | |||
| bc98f5e8fc | |||
| 6f2f6c088b | |||
| da91923577 | |||
| fbc46d8046 | |||
| e8a0fae557 | |||
| e0baf45247 | |||
| 43e983e27a | |||
| b882806847 | |||
| 58c30cd7a6 | |||
| ae1b6d95b8 | |||
| 403af78ba9 | |||
| 59fe690a88 | |||
| a15b005d95 | |||
| b94c396d4e | |||
| 8afc4f5ad1 | |||
| f68b2de9e3 | |||
| f4e29d90e0 | |||
| 5ad5e945c4 | |||
| 4ef7b7735e | |||
| db538d95ba | |||
| cef7cd1ca7 | |||
| 726416fe0c | |||
| 1957d8bb3b | |||
| fab30d353a | |||
| 75990b5961 | |||
| 18900d0e25 | |||
| d08f4230c8 | |||
| 15d06557e9 | |||
| 8d2e708d1c | |||
| 280573be28 | |||
| 415aaf3e2b | |||
| 5081027482 | |||
| 96b24d7ee0 | |||
| ac1785bc08 | |||
| 1da1a20734 | |||
| c765ab9112 | |||
| 664aa3842d | |||
| fbee58e445 | |||
| a5e87dcedf | |||
| 53f9e104ec | |||
| ffe41fe060 | |||
| e89cc24d51 | |||
| 61f3b2ae4a | |||
| a752c7ee4b | |||
| a14f0021bd | |||
| 5eb5549448 | |||
| f5e9151ffd | |||
| 5a9dd11a7c | |||
| 9e6123abd3 | |||
| 6c3ef2aaa9 | |||
| 95ae504c9b | |||
| 88fadcf74e | |||
| bb4994f9fc | |||
| c334f94d72 | |||
| ddf525db40 | |||
| 9c7ef46222 | |||
| eacc7f7fb3 | |||
| 80f597a7eb | |||
| d069da8a4f | |||
| 44ebd8d9ea | |||
| 2fd6dc3603 | |||
| 0629b49112 | |||
| 4958c5a59a | |||
| 523501db8c | |||
| 23cd2b09ca | |||
| a1ff1d4543 | |||
| 1bf2a3b1bf | |||
| 8598ab565c | |||
| a56936b6f9 | |||
| b3e21e0827 | |||
| 739ff33ce1 | |||
| f27a726a6e | |||
| aed906e682 | |||
| e0ee786271 | |||
| 2664a75c84 | |||
| 5d9bde807e | |||
| 0370f9a1c8 | |||
| b2d4eccf7b | |||
| dbece874dd | |||
| 07c714fd11 | |||
| 8e6bac534a | |||
| c5cf1fa6b2 | |||
| 1e4c9c2505 | |||
| a6b56db2f5 | |||
| 43baba1733 | |||
| 95a9e4b5ff | |||
| 9b0aa49d4d | |||
| 7eeb0c4966 | |||
| e9d0aa87bd | |||
| 803ca677c1 | |||
| 68ff77f5b6 | |||
| 47e7469d65 | |||
| 2276584c90 | |||
| 05a0846e89 | |||
| b2947264e1 | |||
| 066927742e | |||
| 4616afced4 |
@@ -1,4 +1,6 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
|
||||
SET release=%1
|
||||
ECHO Installing Npm NuGet Package
|
||||
|
||||
@@ -11,12 +13,9 @@ ECHO Current folder: %CD%
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
|
||||
|
||||
ECHO Temporarily adding Npm and Node to path
|
||||
SET oldPath=%PATH%
|
||||
|
||||
path=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
ECHO %path%
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
SET buildFolder=%CD%
|
||||
|
||||
@@ -29,8 +28,5 @@ call npm install -g grunt-cli --quiet
|
||||
call npm install -g bower --quiet
|
||||
call grunt build --buildversion=%release%
|
||||
|
||||
ECHO Reset path to what it was before
|
||||
path=%oldPath%
|
||||
|
||||
ECHO Move back to the build folder
|
||||
CD %buildFolder%
|
||||
@@ -1,19 +1,19 @@
|
||||
@ECHO OFF
|
||||
SET oldPath=%PATH%
|
||||
SETLOCAL
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
|
||||
git.exe 2> NUL
|
||||
if %ERRORLEVEL%==9009 GOTO :trydefaultpath
|
||||
GOTO :EOF
|
||||
|
||||
:trydefaultpath
|
||||
path=C:\Program Files (x86)\Git\cmd;%PATH%
|
||||
path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
|
||||
git.exe 2> NUL
|
||||
if %ERRORLEVEL%==9009 GOTO :showerror
|
||||
GOTO :EOF
|
||||
|
||||
:showerror
|
||||
path=%oldPath%
|
||||
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\bin
|
||||
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
|
||||
set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
|
||||
if %install%==y (
|
||||
GOTO :installgit
|
||||
@@ -29,5 +29,4 @@ GOTO :EOF
|
||||
ECHO Installing Chocolatey first
|
||||
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
|
||||
ECHO Installing Git through Chocolatey
|
||||
choco install git
|
||||
path=C:\Program Files (x86)\Git\cmd;%path%
|
||||
choco install git
|
||||
@@ -54,7 +54,7 @@
|
||||
</providers>
|
||||
</siteMap>
|
||||
<httpRuntime xdt:Transform="InsertIfMissing" />
|
||||
<httpRuntime targetFramework="4.5" xdt:Transform="SetAttributes(targetFramework)" />
|
||||
<httpRuntime targetFramework="4.5" maxRequestLength="1048576" 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"/>>
|
||||
@@ -140,6 +140,13 @@
|
||||
<remove name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
</handlers>
|
||||
|
||||
<security xdt:Transform="InsertIfMissing">
|
||||
<requestFiltering xdt:Transform="InsertIfMissing">
|
||||
<requestLimits maxAllowedContentLength="1073741824" xdt:Transform="InsertIfMissing" />
|
||||
</requestFiltering>
|
||||
</security>
|
||||
|
||||
</system.webServer>
|
||||
|
||||
<system.codedom xdt:Transform="Remove" />
|
||||
@@ -151,20 +158,26 @@
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.WebPages.Razor')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.OAuth')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.Cookies')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
@@ -177,6 +190,10 @@
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.3.4
|
||||
7.3.5
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.3.4")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.4")]
|
||||
[assembly: AssemblyFileVersion("7.3.5")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.5")]
|
||||
@@ -406,7 +406,8 @@ namespace Umbraco.Core
|
||||
//clear the cache
|
||||
if (ApplicationCache != null)
|
||||
{
|
||||
ApplicationCache.ClearAllCache();
|
||||
ApplicationCache.RuntimeCache.ClearAllCache();
|
||||
ApplicationCache.IsolatedRuntimeCache.ClearAllCaches();
|
||||
}
|
||||
//reset all resolvers
|
||||
ResolverCollection.ResetAll();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constants storing cache keys used in caching
|
||||
/// </summary>
|
||||
@@ -12,52 +13,78 @@ namespace Umbraco.Core.Cache
|
||||
public const string ApplicationsCacheKey = "ApplicationCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserTypeCacheKey = "UserTypeCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future - it is referenced but no cache is stored against this key")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string ContentItemCacheKey = "contentItem";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")]
|
||||
public const string MediaCacheKey = "UL_GetMedia";
|
||||
|
||||
public const string MacroXsltCacheKey = "macroXslt_";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string MacroCacheKey = "UmbracoMacroCache";
|
||||
|
||||
public const string MacroHtmlCacheKey = "macroHtml_";
|
||||
public const string MacroControlCacheKey = "macroControl_";
|
||||
public const string MacroHtmlDateAddedCacheKey = "macroHtml_DateAdded_";
|
||||
public const string MacroControlDateAddedCacheKey = "macroControl_DateAdded_";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy 'library' member caching, remove in v8")]
|
||||
public const string MemberLibraryCacheKey = "UL_GetMember";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
|
||||
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")]
|
||||
public const string TemplateFrontEndCacheKey = "template";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserContextCacheKey = "UmbracoUserContext";
|
||||
|
||||
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserCacheKey = "UmbracoUser";
|
||||
|
||||
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string ContentTypeCacheKey = "UmbracoContentType";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string LanguageCacheKey = "UmbracoLanguageCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string DomainCacheKey = "UmbracoDomainList";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string StylesheetCacheKey = "UmbracoStylesheet";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Sync;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
@@ -63,5 +64,15 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RefreshById));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for all repository entities of this type
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
internal void ClearAllIsolatedCacheByEntityType<TEntity>()
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache<TEntity>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -2,11 +2,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
|
||||
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// The default cache policy for retrieving a single entity
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<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");
|
||||
if (options == null) throw new ArgumentNullException("options");
|
||||
|
||||
_options = options;
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
public string GetCacheIdKey(object id)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException("id");
|
||||
|
||||
return string.Format("{0}{1}", GetCacheTypeKey(), id);
|
||||
}
|
||||
|
||||
public string GetCacheTypeKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public 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(() =>
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity);
|
||||
}
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetCacheIdKey(entity.Id));
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public 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(() =>
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
if (fromCache != null)
|
||||
return fromCache;
|
||||
|
||||
var entity = getFromRepo(id);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(cacheKey, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
return Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
}
|
||||
|
||||
public bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
return fromCache != null || getFromRepo(id);
|
||||
}
|
||||
|
||||
public virtual TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
var entities = ids.Select(Get).ToArray();
|
||||
if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
{
|
||||
if (_options.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var totalCount = _options.PerformCount();
|
||||
if (allEntities.Length == totalCount)
|
||||
return allEntities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
//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(ids)
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(ids, entityCollection);
|
||||
|
||||
return entityCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the lookup for all entities of this type from the cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual 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
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected virtual void SetCacheAction(string cacheKey, TEntity entity)
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for an entity collection
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="entityCollection"></param>
|
||||
protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//This option cannot execute if we are looking up specific Ids
|
||||
if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//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
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is the default behavior, we'll individually cache each item so that if/when these items are resolved
|
||||
// by id, they are returned from the already existing cache.
|
||||
foreach (var entity in entityCollection.WhereNotNull())
|
||||
{
|
||||
var localCopy = entity;
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (localCopy.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
protected void SetCacheAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class DefaultRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new DefaultRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that caches an entire dataset as a single collection
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache) : base(cache, new RepositoryCachePolicyOptions())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For this type of caching policy, we don't cache individual items
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected override void SetCacheAction(string cacheKey, TEntity entity)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
//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));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This policy will cache the full data set as a single collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override TEntity[] GetAllFromCache()
|
||||
{
|
||||
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
|
||||
return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
|
||||
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId> : IDisposable
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
|
||||
TEntity Get(TId id);
|
||||
bool Exists(TId id, Func<TId, bool> getFromRepo);
|
||||
|
||||
string GetCacheIdKey(object id);
|
||||
string GetCacheTypeKey();
|
||||
void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
|
||||
void Remove(TEntity entity, Action<TEntity> persistMethod);
|
||||
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicyFactory<TEntity, TId> where TEntity : class, IAggregateRoot
|
||||
{
|
||||
IRepositoryCachePolicy<TEntity, TId> CreatePolicy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to get/create/manipulate isolated runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
|
||||
/// that the repository doesn't need to search through all keys on a global scale.
|
||||
/// </remarks>
|
||||
public class IsolatedRuntimeCache
|
||||
{
|
||||
private readonly Func<Type, IRuntimeCacheProvider> _cacheFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
|
||||
{
|
||||
_cacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache<T>()
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => _cacheFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache(Type type)
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(type, t => _cacheFactory(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache by the type specified
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public Attempt<IRuntimeCacheProvider> GetCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
return Attempt.Succeed(cache);
|
||||
}
|
||||
return Attempt<IRuntimeCacheProvider>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all values inside this isolated runtime cache
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public void ClearCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all of the isolated caches
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var key in _isolatedCache.Keys)
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryRemove(key, out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class OnlySingleItemsRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new SingleItemsOnlyRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-11
@@ -1,17 +1,34 @@
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class RepositoryCacheOptions
|
||||
internal class RepositoryCachePolicyOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor sets defaults
|
||||
/// Ctor - sets GetAllCacheValidateCount = true
|
||||
/// </summary>
|
||||
public RepositoryCacheOptions()
|
||||
public RepositoryCachePolicyOptions(Func<int> performCount)
|
||||
{
|
||||
PerformCount = performCount;
|
||||
GetAllCacheValidateCount = true;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
GetAllCacheThresholdLimit = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor - sets GetAllCacheValidateCount = false
|
||||
/// </summary>
|
||||
public RepositoryCachePolicyOptions()
|
||||
{
|
||||
PerformCount = null;
|
||||
GetAllCacheValidateCount = false;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback required to get count for GetAllCacheValidateCount
|
||||
/// </summary>
|
||||
public Func<int> PerformCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True/false as to validate the total item count when all items are returned from cache, the default is true but this
|
||||
/// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the normal
|
||||
@@ -21,16 +38,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// setting this to return false will improve performance of GetAll cache with no params but should only be used
|
||||
/// for specific circumstances
|
||||
/// </remarks>
|
||||
public bool GetAllCacheValidateCount { get; set; }
|
||||
public bool GetAllCacheValidateCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the GetAll method will cache that there are zero results so that the db is not hit when there are no results found
|
||||
/// </summary>
|
||||
public bool GetAllCacheAllowZeroCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The threshold entity count for which the GetAll method will cache entities
|
||||
/// </summary>
|
||||
public int GetAllCacheThresholdLimit { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
-203
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -11,20 +13,19 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Class that is exposed by the ApplicationContext for application wide caching purposes
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
private readonly bool _enableCache;
|
||||
private readonly IsolatedRuntimeCache _isolatedCacheManager;
|
||||
private readonly ICacheProvider _requestCache;
|
||||
private readonly ICacheProvider _nullRequestCache = new NullCacheProvider();
|
||||
private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
|
||||
private readonly ICacheProvider _staticCache;
|
||||
private readonly ICacheProvider _nullStaticCache = new NullCacheProvider();
|
||||
private readonly IRuntimeCacheProvider _httpCache;
|
||||
private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider();
|
||||
|
||||
private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a cache helper with disabled caches
|
||||
/// </summary>
|
||||
@@ -34,7 +35,7 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static CacheHelper CreateDisabledCacheHelper()
|
||||
{
|
||||
return new CacheHelper(null, null, null, false);
|
||||
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +45,8 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider())
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,51 +58,42 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider())
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
[Obsolete("Use the constructor the specifies all dependencies")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider)
|
||||
: this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true)
|
||||
: this(httpCacheProvider, staticCacheProvider, requestCacheProvider, new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private ctor used for creating a disabled cache helper
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
/// <param name="enableCache"></param>
|
||||
private CacheHelper(
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
/// <param name="isolatedCacheManager"></param>
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider,
|
||||
bool enableCache)
|
||||
ICacheProvider requestCacheProvider,
|
||||
IsolatedRuntimeCache isolatedCacheManager)
|
||||
{
|
||||
if (enableCache)
|
||||
{
|
||||
_httpCache = httpCacheProvider;
|
||||
_staticCache = staticCacheProvider;
|
||||
_requestCache = requestCacheProvider;
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache = null;
|
||||
_staticCache = null;
|
||||
_requestCache = null;
|
||||
}
|
||||
|
||||
_enableCache = enableCache;
|
||||
if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider");
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -108,15 +101,15 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public ICacheProvider RequestCache
|
||||
{
|
||||
get { return _enableCache ? _requestCache : _nullRequestCache; }
|
||||
get { return _requestCache; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache
|
||||
{
|
||||
get { return _enableCache ? _staticCache : _nullStaticCache; }
|
||||
get { return _staticCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -124,8 +117,16 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
get { return _enableCache ? _httpCache : _nullHttpCache; }
|
||||
get { return _runtimeCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Isolated Runtime cache manager
|
||||
/// </summary>
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache
|
||||
{
|
||||
get { return _isolatedCacheManager; }
|
||||
}
|
||||
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
|
||||
@@ -133,16 +134,11 @@ namespace Umbraco.Core
|
||||
/// Clears the item in umbraco's runtime cache
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearAllCache()
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearAllCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearAllCache();
|
||||
}
|
||||
_runtimeCache.ClearAllCache();
|
||||
_isolatedCacheManager.ClearAllCaches();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -150,16 +146,10 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheItem(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheItem(key);
|
||||
}
|
||||
_runtimeCache.ClearCacheItem(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -171,30 +161,17 @@ namespace Umbraco.Core
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
_runtimeCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
_runtimeCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,16 +179,10 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
_runtimeCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,29 +190,17 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.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)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
return _runtimeCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -251,16 +210,10 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -271,16 +224,11 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,17 +240,12 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
TimeSpan timeout, Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,18 +258,13 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -340,18 +278,13 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
return _runtimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -373,20 +306,13 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -404,20 +330,13 @@ namespace Umbraco.Core
|
||||
CacheDependency cacheDependency,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, null, false, priority, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -427,18 +346,14 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
_runtimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -449,19 +364,14 @@ namespace Umbraco.Core
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
_runtimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -480,19 +390,12 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles:null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -513,19 +416,12 @@ namespace Umbraco.Core
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = _runtimeCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A List that can be deep cloned with deep cloned elements and can reset the collection's items dirty flags
|
||||
/// </summary>
|
||||
/// <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()
|
||||
{
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new list and adds each element as a deep cloned element if it is of type IDeepCloneable
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object DeepClone()
|
||||
{
|
||||
var newList = new DeepCloneableList<T>();
|
||||
foreach (var item in this)
|
||||
{
|
||||
var dc = item as IDeepCloneable;
|
||||
if (dc != null)
|
||||
{
|
||||
newList.Add((T) dc.DeepClone());
|
||||
}
|
||||
else
|
||||
{
|
||||
newList.Add(item);
|
||||
}
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
|
||||
public bool IsDirty()
|
||||
{
|
||||
return this.OfType<IRememberBeingDirty>().Any(x => x.IsDirty());
|
||||
}
|
||||
|
||||
public bool WasDirty()
|
||||
{
|
||||
return this.OfType<IRememberBeingDirty>().Any(x => x.WasDirty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false, the list has no properties we need to report
|
||||
/// </summary>
|
||||
/// <param name="propName"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsPropertyDirty(string propName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false, the list has no properties we need to report
|
||||
/// </summary>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool WasPropertyDirty(string propertyName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties()
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public void ForgetPreviouslyDirtyProperties()
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ForgetPreviouslyDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.3.4");
|
||||
private static readonly Version Version = new Version("7.3.5");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
internal const string StandardPropertiesGroupName = "Membership";
|
||||
|
||||
internal static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
|
||||
public static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
|
||||
{
|
||||
return new Dictionary<string, PropertyType>
|
||||
{
|
||||
|
||||
@@ -188,10 +188,16 @@ namespace Umbraco.Core
|
||||
protected virtual CacheHelper CreateApplicationCache()
|
||||
{
|
||||
var cacheHelper = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
//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()),
|
||||
new StaticCacheProvider(),
|
||||
//we have no request based cache when not running in web-based context
|
||||
new NullCacheProvider());
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -199,8 +199,8 @@ namespace Umbraco.Core.IO
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
var thumbnail = maxWidthHeight == -1
|
||||
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
|
||||
|
||||
@@ -11,8 +11,12 @@ namespace Umbraco.Core.Macros
|
||||
/// </summary>
|
||||
internal class MacroTagParser
|
||||
{
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO (?:.+?)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
|
||||
|
||||
private static readonly Regex MacroPersistedFormat =
|
||||
new Regex(@"(<\?UMBRACO_MACRO (?:.+?)??macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
items.Add (key, new WindowsByteString (key, value));
|
||||
} else {
|
||||
items.Add (key, new ExifAscii (key, value, parent.Encoding));
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Media
|
||||
var fileHeight = image.Height;
|
||||
return new Size(fileWidth, fileHeight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static string GetMimeType(this Image image)
|
||||
@@ -79,22 +79,22 @@ namespace Umbraco.Core.Media
|
||||
/// <param name="additionalThumbSizes"></param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<ResizedImage> GenerateMediaThumbnails(
|
||||
IFileSystem fs,
|
||||
string fileName,
|
||||
string extension,
|
||||
IFileSystem fs,
|
||||
string fileName,
|
||||
string extension,
|
||||
Image originalImage,
|
||||
IEnumerable<int> additionalThumbSizes)
|
||||
{
|
||||
|
||||
var result = new List<ResizedImage>();
|
||||
|
||||
var allSizesDictionary = new Dictionary<int,string> {{100,"thumb"}, {500,"big-thumb"}};
|
||||
|
||||
var allSizesDictionary = new Dictionary<int, string> { { 100, "thumb" }, { 500, "big-thumb" } };
|
||||
|
||||
//combine the static dictionary with the additional sizes with only unique values
|
||||
var allSizes = allSizesDictionary.Select(kv => kv.Key)
|
||||
.Union(additionalThumbSizes.Where(x => x > 0).Distinct());
|
||||
|
||||
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s]: "");
|
||||
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s] : "");
|
||||
|
||||
foreach (var s in sizesDictionary)
|
||||
{
|
||||
@@ -121,9 +121,9 @@ namespace Umbraco.Core.Media
|
||||
/// <returns></returns>
|
||||
private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
|
||||
{
|
||||
var fileNameThumb = String.IsNullOrEmpty(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf(".")))
|
||||
: string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
var thumb = GenerateThumbnail(
|
||||
originalImage,
|
||||
@@ -190,9 +190,9 @@ namespace Umbraco.Core.Media
|
||||
//use best quality
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
}
|
||||
|
||||
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
@@ -202,10 +202,29 @@ namespace Umbraco.Core.Media
|
||||
|
||||
// Copy metadata
|
||||
var imageEncoders = ImageCodecInfo.GetImageEncoders();
|
||||
|
||||
var codec = extension.ToLower() == "png" || extension.ToLower() == "gif"
|
||||
? imageEncoders.Single(t => t.MimeType.Equals("image/png"))
|
||||
: imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
ImageCodecInfo codec;
|
||||
switch (extension.ToLower())
|
||||
{
|
||||
case "png":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
|
||||
break;
|
||||
case "gif":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/gif"));
|
||||
break;
|
||||
case "tif":
|
||||
case "tiff":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/tiff"));
|
||||
break;
|
||||
case "bmp":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/bmp"));
|
||||
break;
|
||||
// TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
|
||||
// place so left it here, but it needs to not set a codec if it doesn't know which one to pick
|
||||
// Note: when fixing this: both .jpg and .jpeg should be handled as extensions
|
||||
default:
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Set compresion ratio to 90%
|
||||
var ep = new EncoderParameters();
|
||||
@@ -213,12 +232,14 @@ namespace Umbraco.Core.Media
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
fs.AddFile(predictableThumbnailNameJpg, ms);
|
||||
}
|
||||
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
|
||||
@@ -78,6 +78,12 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
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
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryItem : Entity, IDictionaryItem
|
||||
{
|
||||
public Func<int, ILanguage> GetLanguage { get; set; }
|
||||
private Guid? _parentId;
|
||||
private string _itemKey;
|
||||
private IEnumerable<IDictionaryTranslation> _translations;
|
||||
@@ -78,7 +79,17 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_translations = value;
|
||||
var asArray = value.ToArray();
|
||||
//ensure the language callback is set on each translation
|
||||
if (GetLanguage != null)
|
||||
{
|
||||
foreach (var translation in asArray.OfType<DictionaryTranslation>())
|
||||
{
|
||||
translation.GetLanguage = GetLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
_translations = asArray;
|
||||
return _translations;
|
||||
}, _translations, TranslationsSelector,
|
||||
//Custom comparer for enumerable
|
||||
|
||||
@@ -13,13 +13,18 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryTranslation : Entity, IDictionaryTranslation
|
||||
{
|
||||
internal Func<int, ILanguage> GetLanguage { get; set; }
|
||||
|
||||
private ILanguage _language;
|
||||
private string _value;
|
||||
//note: this will be memberwise cloned
|
||||
private int _languageId;
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@@ -27,6 +32,20 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value, Guid uniqueId)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
@@ -37,20 +56,43 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone - TODO: this member shouldn't really exist here in the first place, the DictionaryItem
|
||||
/// class will have a deep hierarchy of objects which all get deep cloned which we don't want. This should have simply
|
||||
/// just referenced a language ID not the actual language object. In v8 we need to fix this.
|
||||
/// We're going to have to do the same hacky stuff we had to do with the Template/File contents so that this is returned
|
||||
/// on a callback.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public ILanguage Language
|
||||
{
|
||||
get { return _language; }
|
||||
get
|
||||
{
|
||||
if (_language != null)
|
||||
return _language;
|
||||
|
||||
// else, must lazy-load
|
||||
if (GetLanguage != null && _languageId > 0)
|
||||
_language = GetLanguage(_languageId);
|
||||
return _language;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_language = value;
|
||||
_languageId = _language == null ? -1 : _language.Id;
|
||||
return _language;
|
||||
}, _language, LanguageSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public int LanguageId
|
||||
{
|
||||
get { return _languageId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
@@ -68,5 +110,23 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (DictionaryTranslation)base.DeepClone();
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clone._language = null;
|
||||
|
||||
// turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
|
||||
// this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
// re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace Umbraco.Core.Models
|
||||
[DataMember]
|
||||
ILanguage Language { get; set; }
|
||||
|
||||
int LanguageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Security;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -51,7 +46,7 @@ namespace Umbraco.Core
|
||||
public static Attempt<T> TryConvertTo<T>(this object input)
|
||||
{
|
||||
var result = TryConvertTo(input, typeof(T));
|
||||
if (!result.Success)
|
||||
if (result.Success == false)
|
||||
{
|
||||
//just try a straight up conversion
|
||||
try
|
||||
@@ -64,7 +59,7 @@ namespace Umbraco.Core
|
||||
return Attempt<T>.Fail(e);
|
||||
}
|
||||
}
|
||||
return !result.Success ? Attempt<T>.Fail() : Attempt<T>.Succeed((T)result.Result);
|
||||
return result.Success == false ? Attempt<T>.Fail() : Attempt<T>.Succeed((T)result.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +112,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// we've already dealed with nullables, so any other generic types need to fall through
|
||||
if (!destinationType.IsGenericType)
|
||||
if (destinationType.IsGenericType == false)
|
||||
{
|
||||
if (input is string)
|
||||
{
|
||||
@@ -335,11 +330,11 @@ namespace Umbraco.Core
|
||||
return null; // we can't decide...
|
||||
}
|
||||
|
||||
private readonly static char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
|
||||
private static readonly char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
|
||||
|
||||
private static string NormalizeNumberDecimalSeparator(string s)
|
||||
{
|
||||
var normalized = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator[0];
|
||||
var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
|
||||
return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
|
||||
}
|
||||
|
||||
@@ -442,7 +437,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
var props = TypeDescriptor.GetProperties(o);
|
||||
var d = new Dictionary<string, TVal>();
|
||||
foreach (var prop in props.Cast<PropertyDescriptor>().Where(x => !ignoreProperties.Contains(x.Name)))
|
||||
foreach (var prop in props.Cast<PropertyDescriptor>().Where(x => ignoreProperties.Contains(x.Name) == false))
|
||||
{
|
||||
var val = prop.GetValue(o);
|
||||
if (val != null)
|
||||
@@ -478,13 +473,13 @@ namespace Umbraco.Core
|
||||
|
||||
var items = (from object enumItem in enumerable let value = GetEnumPropertyDebugString(enumItem, levels) where value != null select value).Take(10).ToList();
|
||||
|
||||
return items.Count() > 0
|
||||
return items.Any()
|
||||
? "{{ {0} }}".InvariantFormat(String.Join(", ", items))
|
||||
: null;
|
||||
}
|
||||
|
||||
var props = obj.GetType().GetProperties();
|
||||
if ((props.Count() == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
|
||||
if ((props.Length == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -500,12 +495,12 @@ namespace Umbraco.Core
|
||||
if (levels > -1)
|
||||
{
|
||||
var items =
|
||||
from propertyInfo in props
|
||||
(from propertyInfo in props
|
||||
let value = GetPropertyDebugString(propertyInfo, obj, levels)
|
||||
where value != null
|
||||
select "{0}={1}".InvariantFormat(propertyInfo.Name, value);
|
||||
select "{0}={1}".InvariantFormat(propertyInfo.Name, value)).ToArray();
|
||||
|
||||
return items.Count() > 0
|
||||
return items.Any()
|
||||
? "[{0}]:{{ {1} }}".InvariantFormat(obj.GetType().Name, String.Join(", ", items))
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
var text = new LanguageTextDto
|
||||
{
|
||||
LanguageId = translation.Language.Id,
|
||||
LanguageId = translation.LanguageId,
|
||||
UniqueId = translation.Key,
|
||||
Value = translation.Value
|
||||
};
|
||||
|
||||
@@ -7,20 +7,19 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
internal class DictionaryTranslationFactory
|
||||
{
|
||||
private readonly Guid _uniqueId;
|
||||
private ILanguage _language;
|
||||
|
||||
public DictionaryTranslationFactory(Guid uniqueId, ILanguage language)
|
||||
public DictionaryTranslationFactory(Guid uniqueId)
|
||||
{
|
||||
_uniqueId = uniqueId;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
#region Implementation of IEntityFactory<DictionaryTranslation,LanguageTextDto>
|
||||
|
||||
public IDictionaryTranslation BuildEntity(LanguageTextDto dto)
|
||||
{
|
||||
var item = new DictionaryTranslation(_language, dto.Value, _uniqueId)
|
||||
var item = new DictionaryTranslation(dto.LanguageId, dto.Value, _uniqueId)
|
||||
{Id = dto.PrimaryKey};
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
item.ResetDirtyProperties(false);
|
||||
@@ -31,7 +30,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
var text = new LanguageTextDto
|
||||
{
|
||||
LanguageId = entity.Language.Id,
|
||||
LanguageId = entity.LanguageId,
|
||||
UniqueId = _uniqueId,
|
||||
Value = entity.Value
|
||||
};
|
||||
|
||||
@@ -129,11 +129,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
Name = propertyType.Name,
|
||||
SortOrder = propertyType.SortOrder,
|
||||
ValidationRegExp = propertyType.ValidationRegExp,
|
||||
UniqueId = propertyType.HasIdentity
|
||||
? propertyType.Key == Guid.Empty
|
||||
? Guid.NewGuid()
|
||||
: propertyType.Key
|
||||
: Guid.NewGuid()
|
||||
UniqueId = propertyType.Key == Guid.Empty ? Guid.NewGuid() : propertyType.Key
|
||||
};
|
||||
|
||||
if (tabId != default(int))
|
||||
|
||||
@@ -234,8 +234,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var processed = 0;
|
||||
do
|
||||
{
|
||||
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
|
||||
|
||||
//NOTE: This is an important call, we cannot simply make a call to:
|
||||
// GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
|
||||
// because that method is used to query 'latest' content items where in this case we don't necessarily
|
||||
// want latest content items because a pulished content item might not actually be the latest.
|
||||
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
|
||||
var descendants = GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out total,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, "Path", Direction.Ascending);
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
|
||||
@@ -728,7 +735,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
{
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(content, xml));
|
||||
}
|
||||
|
||||
@@ -826,11 +833,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
|
||||
.ToArray();
|
||||
|
||||
|
||||
|
||||
var ids = dtos
|
||||
.Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
.Select(x => x.TemplateId.Value).ToArray();
|
||||
|
||||
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var templates = ids.Length == 0 ? Enumerable.Empty<ITemplate>() : _templateRepository.GetAll(ids).ToArray();
|
||||
|
||||
@@ -972,4 +979,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentXmlRepository.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,6 +454,7 @@ AND umbracoNode.id <> @id",
|
||||
propType.DataTypeDefinitionId = dto.DataTypeId;
|
||||
propType.Description = dto.Description;
|
||||
propType.Id = dto.Id;
|
||||
propType.Key = dto.UniqueId;
|
||||
propType.Name = dto.Name;
|
||||
propType.Mandatory = dto.Mandatory;
|
||||
propType.SortOrder = dto.SortOrder;
|
||||
|
||||
@@ -24,17 +24,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DataTypeDefinitionRepository : PetaPocoRepositoryBase<int, IDataTypeDefinition>, IDataTypeDefinitionRepository
|
||||
{
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly DataTypePreValueRepository _preValRepository;
|
||||
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax,
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax,
|
||||
IContentTypeRepository contentTypeRepository)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax);
|
||||
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax);
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,DataTypeDefinition>
|
||||
@@ -231,7 +229,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
//NOTE: This is a special case, we need to clear the custom cache for pre-values here so they are not stale if devs
|
||||
// are querying for them in the Saved event (before the distributed call cache is clearing it)
|
||||
_cacheHelper.RuntimeCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id));
|
||||
RuntimeCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
@@ -270,7 +268,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
|
||||
{
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
var cached = RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache, ensure it's a cloned result
|
||||
@@ -289,7 +287,7 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
//We need to see if we can find the cached PreValueCollection based on the cache key above
|
||||
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
|
||||
var cached = RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
@@ -408,7 +406,7 @@ AND umbracoNode.id <> @id",
|
||||
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
|
||||
|
||||
//store into cache
|
||||
_cacheHelper.RuntimeCache.InsertCacheItem(key, () => collection,
|
||||
RuntimeCache.InsertCacheItem(key, () => collection,
|
||||
//30 mins
|
||||
new TimeSpan(0, 0, 30),
|
||||
//sliding is true
|
||||
|
||||
@@ -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;
|
||||
@@ -19,30 +20,40 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DictionaryRepository : PetaPocoRepositoryBase<int, IDictionaryItem>, IDictionaryRepository
|
||||
{
|
||||
private readonly ILanguageRepository _languageRepository;
|
||||
|
||||
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax, ILanguageRepository languageRepository)
|
||||
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax)
|
||||
: base(work, cache, logger, syntax)
|
||||
{
|
||||
_languageRepository = languageRepository;
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, int> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, int>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,DictionaryItem>
|
||||
|
||||
protected override IDictionaryItem PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new {Id = id})
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
var entity = ConvertFromDto(dto, allLanguages);
|
||||
|
||||
var entity = ConvertFromDto(dto);
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
@@ -56,14 +67,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false).Where("cmsDictionary.pk > 0");
|
||||
if (ids.Any())
|
||||
{
|
||||
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
|
||||
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
|
||||
}
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(dto => ConvertFromDto(dto, allLanguages));
|
||||
.Select(dto => ConvertFromDto(dto));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IDictionaryItem> PerformGetByQuery(IQuery<IDictionaryItem> query)
|
||||
@@ -72,12 +80,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(x => ConvertFromDto(x, allLanguages));
|
||||
.Select(x => ConvertFromDto(x));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -87,7 +92,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
if(isCount)
|
||||
if (isCount)
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<DictionaryDto>(SqlSyntax);
|
||||
@@ -123,26 +128,28 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override void PersistNewItem(IDictionaryItem entity)
|
||||
{
|
||||
((DictionaryItem)entity).AddingEntity();
|
||||
var dictionaryItem = ((DictionaryItem) entity);
|
||||
|
||||
foreach (var translation in entity.Translations)
|
||||
dictionaryItem.AddingEntity();
|
||||
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
var factory = new DictionaryItemFactory();
|
||||
var dto = factory.BuildDto(entity);
|
||||
var dto = factory.BuildDto(dictionaryItem);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
entity.Id = id;
|
||||
dictionaryItem.Id = id;
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key, null);
|
||||
foreach (var translation in entity.Translations)
|
||||
var translationFactory = new DictionaryTranslationFactory(dictionaryItem.Key);
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
{
|
||||
var textDto = translationFactory.BuildDto(translation);
|
||||
translation.Id = Convert.ToInt32(Database.Insert(textDto));
|
||||
translation.Key = entity.Key;
|
||||
translation.Key = dictionaryItem.Key;
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
dictionaryItem.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IDictionaryItem entity)
|
||||
@@ -157,11 +164,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
Database.Update(dto);
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key, null);
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key);
|
||||
foreach (var translation in entity.Translations)
|
||||
{
|
||||
var textDto = translationFactory.BuildDto(translation);
|
||||
if(translation.HasIdentity)
|
||||
if (translation.HasIdentity)
|
||||
{
|
||||
Database.Update(textDto);
|
||||
}
|
||||
@@ -183,7 +190,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
RecursiveDelete(entity.Key);
|
||||
|
||||
Database.Delete<LanguageTextDto>("WHERE UniqueId = @Id", new { Id = entity.Key});
|
||||
Database.Delete<LanguageTextDto>("WHERE UniqueId = @Id", new { Id = entity.Key });
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = entity.Key });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
@@ -193,7 +200,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private void RecursiveDelete(Guid parentId)
|
||||
{
|
||||
var list = Database.Fetch<DictionaryDto>("WHERE parent = @ParentId", new {ParentId = parentId});
|
||||
var list = Database.Fetch<DictionaryDto>("WHERE parent = @ParentId", new { ParentId = parentId });
|
||||
foreach (var dto in list)
|
||||
{
|
||||
RecursiveDelete(dto.UniqueId);
|
||||
@@ -209,20 +216,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#endregion
|
||||
|
||||
protected IDictionaryItem ConvertFromDto(DictionaryDto dto, ILanguage[] allLanguages)
|
||||
protected IDictionaryItem ConvertFromDto(DictionaryDto dto)
|
||||
{
|
||||
var factory = new DictionaryItemFactory();
|
||||
var entity = factory.BuildEntity(dto);
|
||||
|
||||
var list = new List<IDictionaryTranslation>();
|
||||
foreach (var textDto in dto.LanguageTextDtos)
|
||||
{
|
||||
//Assuming this is cached!
|
||||
var language = allLanguages.FirstOrDefault(x => x.Id == textDto.LanguageId);
|
||||
if (language == null)
|
||||
{
|
||||
if (textDto.LanguageId <= 0)
|
||||
continue;
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(dto.UniqueId, language);
|
||||
var translationFactory = new DictionaryTranslationFactory(dto.UniqueId);
|
||||
list.Add(translationFactory.BuildEntity(textDto));
|
||||
}
|
||||
entity.Translations = list;
|
||||
@@ -234,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
using (var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax))
|
||||
{
|
||||
return uniqueIdRepo.Get(uniqueId);
|
||||
return uniqueIdRepo.Get(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +247,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
using (var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax))
|
||||
{
|
||||
return keyRepo.Get(key);
|
||||
return keyRepo.Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<IDictionaryItem> GetRootDictionaryItems()
|
||||
{
|
||||
var query = Query<IDictionaryItem>.Builder.Where(x => x.ParentId == null);
|
||||
@@ -254,9 +259,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
//This methods will look up children at each level, since we do not store a path for dictionary (ATM), we need to do a recursive
|
||||
// lookup to get descendants. Currently this is the most efficient way to do it
|
||||
|
||||
@@ -275,7 +277,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(x => ConvertFromDto(x, allLanguages));
|
||||
.Select(x => ConvertFromDto(x));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -284,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
: getItemsFromParents(new[] { parentId.Value });
|
||||
|
||||
return childItems.SelectRecursive(items => getItemsFromParents(items.Select(x => x.Key).ToArray())).SelectMany(items => items);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class DictionaryByUniqueIdRepository : SimpleGetRepository<Guid, IDictionaryItem, DictionaryDto>
|
||||
@@ -314,20 +316,34 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IDictionaryItem ConvertToEntity(DictionaryDto dto)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _dictionaryRepository._languageRepository.GetAll().ToArray();
|
||||
return _dictionaryRepository.ConvertFromDto(dto, allLanguages);
|
||||
return _dictionaryRepository.ConvertFromDto(dto);
|
||||
}
|
||||
|
||||
protected override object GetBaseWhereClauseArguments(Guid id)
|
||||
{
|
||||
return new {Id = id};
|
||||
return new { Id = id };
|
||||
}
|
||||
|
||||
protected override string GetWhereInClauseForGetAll()
|
||||
{
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, Guid> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, Guid>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryByKeyRepository : SimpleGetRepository<string, IDictionaryItem, DictionaryDto>
|
||||
@@ -357,9 +373,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IDictionaryItem ConvertToEntity(DictionaryDto dto)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _dictionaryRepository._languageRepository.GetAll().ToArray();
|
||||
return _dictionaryRepository.ConvertFromDto(dto, allLanguages);
|
||||
return _dictionaryRepository.ConvertFromDto(dto);
|
||||
}
|
||||
|
||||
protected override object GetBaseWhereClauseArguments(string id)
|
||||
@@ -371,17 +385,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, string> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, string> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, string>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ensure the unit of work is disposed
|
||||
/// </remarks>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_languageRepository.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,25 +18,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
internal class DomainRepository : PetaPocoRepositoryBase<int, IDomain>, IDomainRepository
|
||||
{
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
//Custom cache options for better performance
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<IDomain, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDomain, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IDomain PerformGet(int id)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
@@ -22,6 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
TemplateNode GetTemplateNode(string alias);
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
@@ -20,23 +21,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
//Custom cache options for better performance
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<ILanguage, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ILanguage, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,Language>
|
||||
@@ -159,13 +154,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public ILanguage GetByCultureName(string cultureName)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all domains
|
||||
//use the underlying GetAll which will force cache all languages
|
||||
return GetAll().FirstOrDefault(x => x.CultureName.InvariantEquals(cultureName));
|
||||
}
|
||||
|
||||
public ILanguage GetByIsoCode(string isoCode)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all domains
|
||||
//use the underlying GetAll which will force cache all languages
|
||||
return GetAll().FirstOrDefault(x => x.IsoCode.InvariantEquals(isoCode));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
internal class MemberGroupRepository : PetaPocoRepositoryBase<int, IMemberGroup>, IMemberGroupRepository
|
||||
{
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
|
||||
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, CacheHelper cacheHelper)
|
||||
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
|
||||
_cacheHelper = cacheHelper;
|
||||
}
|
||||
|
||||
private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory();
|
||||
@@ -135,7 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IMemberGroup GetByName(string name)
|
||||
{
|
||||
return _cacheHelper.RuntimeCache.GetCacheItem<IMemberGroup>(
|
||||
return RuntimeCache.GetCacheItem<IMemberGroup>(
|
||||
string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name),
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -26,13 +26,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IDatabaseUnitOfWork _unitOfWork;
|
||||
private readonly CacheHelper _cache;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
|
||||
internal PermissionRepository(IDatabaseUnitOfWork unitOfWork, CacheHelper cache, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_cache = cache;
|
||||
//Make this repository use an isolated cache
|
||||
_runtimeCache = cache.IsolatedRuntimeCache.GetOrCreateCache<EntityPermission>();
|
||||
_sqlSyntax = sqlSyntax;
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public IEnumerable<EntityPermission> GetUserPermissionsForEntities(int userId, params int[] entityIds)
|
||||
{
|
||||
var entityIdKey = string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture)));
|
||||
return _cache.RuntimeCache.GetCacheItem<IEnumerable<EntityPermission>>(
|
||||
return _runtimeCache.GetCacheItem<IEnumerable<EntityPermission>>(
|
||||
string.Format("{0}{1}{2}", CacheKeys.UserPermissionsCacheKey, userId, entityIdKey),
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -17,33 +17,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_options = new RepositoryCacheOptions
|
||||
{
|
||||
//We want to ensure that a zero count gets cached, even if there is nothing in the db we don't want it to lookup nothing each time
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
//Set to 1000 just to ensure that all of them are cached, The GetAll on this repository gets called *A lot*, we want max performance
|
||||
GetAllCacheThresholdLimit = 1000,
|
||||
//Override to false so that a Count check against the db is NOT performed when doing a GetAll without params, we just want to
|
||||
// return the raw cache without validation. The GetAll on this repository gets called *A lot*, we want max performance
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _options;
|
||||
private FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<PublicAccessEntry, Guid> CachePolicyFactory
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
protected override PublicAccessEntry PerformGet(Guid id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var taskDto = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql).FirstOrDefault();
|
||||
if (taskDto == null)
|
||||
return null;
|
||||
|
||||
var factory = new PublicAccessEntryFactory();
|
||||
var entity = factory.BuildEntity(taskDto);
|
||||
return entity;
|
||||
//return from GetAll - this will be cached as a collection
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<PublicAccessEntry> PerformGetAll(params Guid[] ids)
|
||||
@@ -102,15 +92,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
{
|
||||
get { return _options; }
|
||||
}
|
||||
|
||||
|
||||
protected override void PersistNewItem(PublicAccessEntry entity)
|
||||
{
|
||||
entity.AddingEntity();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
@@ -22,11 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
Logger = logger;
|
||||
_work = work;
|
||||
|
||||
//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.
|
||||
_cache = new CacheHelper(new DeepCloneRuntimeCacheProvider(cache.RuntimeCache), cache.StaticCache, cache.RequestCache);
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,9 +82,37 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _cacheOptions = new RepositoryCacheOptions();
|
||||
|
||||
|
||||
#region IRepository<TEntity> Members
|
||||
/// <summary>
|
||||
/// The runtime cache used for this repo by default is the isolated cache for this type
|
||||
/// </summary>
|
||||
protected override IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>(); }
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<TEntity, TId> _cachePolicyFactory;
|
||||
/// <summary>
|
||||
/// Returns the Cache Policy for the repository
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Cache Policy determines how each entity or entity collection is cached
|
||||
/// </remarks>
|
||||
protected virtual IRepositoryCachePolicyFactory<TEntity, TId> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new DefaultRepositoryCachePolicyFactory<TEntity, TId>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions(() =>
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var query = Query<TEntity>.Builder.Where(x => x.Id != 0);
|
||||
return PerformCount(query);
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or Updates an entity of type TEntity
|
||||
@@ -119,23 +145,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected abstract TEntity PerformGet(TId id);
|
||||
/// <summary>
|
||||
/// Gets an entity by the passed in Id utilizing the repository's runtime cache
|
||||
/// Gets an entity by the passed in Id utilizing the repository's cache policy
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey<TEntity>(id);
|
||||
var fromCache = RuntimeCache.GetCacheItem<TEntity>(cacheKey);
|
||||
|
||||
if (fromCache != null) return fromCache;
|
||||
|
||||
var entity = PerformGet(id);
|
||||
if (entity == null) return null;
|
||||
|
||||
RuntimeCache.InsertCacheItem(cacheKey, () => entity);
|
||||
|
||||
return entity;
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return p.Get(id, PerformGet);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[] ids);
|
||||
@@ -158,88 +177,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
throw new InvalidOperationException("Cannot perform a query with more than 2000 parameters");
|
||||
}
|
||||
|
||||
if (ids.Any())
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
var entities = ids.Select(x => RuntimeCache.GetCacheItem<TEntity>(GetCacheIdKey<TEntity>(x))).ToArray();
|
||||
|
||||
if (ids.Count().Equals(entities.Count()) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = RuntimeCache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey<TEntity>())
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
if (allEntities.Any())
|
||||
{
|
||||
|
||||
if (RepositoryCacheOptions.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var query = Query<TEntity>.Builder.Where(x => x.Id != 0);
|
||||
int totalCount = PerformCount(query);
|
||||
|
||||
if (allEntities.Count() == totalCount)
|
||||
return allEntities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
}
|
||||
}
|
||||
else if (RepositoryCacheOptions.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//if the repository allows caching a zero count, then check the zero count cache
|
||||
var zeroCount = RuntimeCache.GetCacheItem<TEntity[]>(GetCacheTypeKey<TEntity>());
|
||||
if (zeroCount != null && zeroCount.Any() == false)
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return Enumerable.Empty<TEntity>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var entityCollection = PerformGetAll(ids)
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//We need to put a threshold here! IF there's an insane amount of items
|
||||
// coming back here we don't want to chuck it all into memory, this added cache here
|
||||
// is more for convenience when paging stuff temporarily
|
||||
|
||||
if (entityCollection.Length > RepositoryCacheOptions.GetAllCacheThresholdLimit)
|
||||
return entityCollection;
|
||||
|
||||
if (entityCollection.Length == 0 && RepositoryCacheOptions.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//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
|
||||
RuntimeCache.InsertCacheItem(GetCacheTypeKey<TEntity>(), () => new TEntity[] {});
|
||||
}
|
||||
|
||||
foreach (var entity in entityCollection)
|
||||
{
|
||||
if (entity != null)
|
||||
{
|
||||
var localCopy = entity;
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => localCopy);
|
||||
}
|
||||
}
|
||||
|
||||
return entityCollection;
|
||||
return p.GetAll(ids, PerformGetAll);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected virtual RepositoryCacheOptions RepositoryCacheOptions
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
|
||||
/// <summary>
|
||||
/// Gets a list of entities by the passed in query
|
||||
@@ -261,12 +204,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public bool Exists(TId id)
|
||||
{
|
||||
var fromCache = RuntimeCache.GetCacheItem<TEntity>(GetCacheIdKey<TEntity>(id));
|
||||
if (fromCache != null)
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return true;
|
||||
return p.Exists(id, PerformExists);
|
||||
}
|
||||
return PerformExists(id);
|
||||
}
|
||||
|
||||
protected abstract int PerformCount(IQuery<TEntity> query);
|
||||
@@ -279,34 +220,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return PerformCount(query);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUnitOfWorkRepository Members
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unit of work method that tells the repository to persist the new entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistNewItem(IEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
PersistNewItem((TEntity)entity);
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => entity);
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
throw;
|
||||
}
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistNewItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,23 +241,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistUpdatedItem(IEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
PersistUpdatedItem((TEntity)entity);
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => entity);
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
throw;
|
||||
}
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistUpdatedItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -340,21 +255,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistDeletedItem(IEntity entity)
|
||||
{
|
||||
PersistDeletedItem((TEntity)entity);
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.Remove(casted, PersistDeletedItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract IUnitOfWorkRepository Methods
|
||||
|
||||
|
||||
protected abstract void PersistNewItem(TEntity item);
|
||||
protected abstract void PersistUpdatedItem(TEntity item);
|
||||
protected abstract void PersistDeletedItem(TEntity item);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -32,7 +33,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private readonly ITemplatesSection _templateConfig;
|
||||
private readonly ViewHelper _viewHelper;
|
||||
private readonly MasterPageHelper _masterPageHelper;
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
@@ -41,41 +41,26 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_viewsFileSystem = viewFileSystem;
|
||||
_templateConfig = templateConfig;
|
||||
_viewHelper = new ViewHelper(_viewsFileSystem);
|
||||
_masterPageHelper = new MasterPageHelper(_masterpagesFileSystem);
|
||||
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
//Allow a zero count cache entry because GetAll() gets used quite a lot and we want to ensure
|
||||
// if there are no templates, that it doesn't keep going to the db.
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
//GetAll gets called a lot, we want to ensure that all templates are in the cache, default is 100 which
|
||||
// would normally be fine but we'll increase it in case people have a ton of templates.
|
||||
GetAllCacheThresholdLimit = 500
|
||||
};
|
||||
_masterPageHelper = new MasterPageHelper(_masterpagesFileSystem);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<ITemplate, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ITemplate, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,ITemplate>
|
||||
|
||||
protected override ITemplate PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.NodeId == id);
|
||||
var result = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
|
||||
if (result == null) return null;
|
||||
|
||||
//look up the simple template definitions that have a master template assigned, this is used
|
||||
// later to populate the template item's properties
|
||||
var childIds = GetAxisDefinitions(result).ToArray();
|
||||
|
||||
return MapFromDto(result, childIds);
|
||||
//use the underlying GetAll which will force cache all templates
|
||||
return base.GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ITemplate> PerformGetAll(params int[] ids)
|
||||
@@ -479,126 +464,102 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public ITemplate Get(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.Alias == alias);
|
||||
|
||||
var dto = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
return MapFromDto(dto, GetAxisDefinitions(dto).ToArray());
|
||||
return GetAll(alias).FirstOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetAll(params string[] aliases)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//We must call the base (normal) GetAll method
|
||||
// which is cached. This is a specialized method and unfortunatley with the params[] it
|
||||
// overlaps with the normal GetAll method.
|
||||
if (aliases.Any() == false) return base.GetAll();
|
||||
|
||||
if (aliases.Any())
|
||||
{
|
||||
sql.Where("cmsTemplate.alias IN (@aliases)", new {aliases = aliases});
|
||||
}
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => aliases.Contains(x.Alias));
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetChildren(int masterTemplateId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
if (masterTemplateId <= 0)
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId <= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId == masterTemplateId);
|
||||
}
|
||||
//return from base.GetAll, this is all cached
|
||||
var all = base.GetAll().ToArray();
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
if (masterTemplateId <= 0) return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace());
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
var parent = all.FirstOrDefault(x => x.Id == masterTemplateId);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var children = all.Where(x => x.MasterTemplateAlias == parent.Alias);
|
||||
return children;
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetChildren(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
if (alias.IsNullOrWhiteSpace())
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId <= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var parent = Database.ExecuteScalar<int?>(new Sql().Select("nodeId").From<TemplateDto>(SqlSyntax).Where<TemplateDto>(dto => dto.Alias == alias));
|
||||
if (parent.HasValue == false) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where<NodeDto>(x => x.ParentId == parent.Value);
|
||||
}
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => alias.IsNullOrWhiteSpace()
|
||||
? x.MasterTemplateAlias.IsNullOrWhiteSpace()
|
||||
: x.MasterTemplateAlias == alias);
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetDescendants(int masterTemplateId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//return from base.GetAll, this is all cached
|
||||
var all = base.GetAll().ToArray();
|
||||
var descendants = new List<ITemplate>();
|
||||
if (masterTemplateId > 0)
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var path = Database.ExecuteScalar<string>(
|
||||
new Sql().Select(SqlSyntax.GetQuotedColumnName("path"))
|
||||
.From<TemplateDto>(SqlSyntax)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<TemplateDto, NodeDto>(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeId == masterTemplateId));
|
||||
|
||||
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new { query = path + ",%" });
|
||||
var parent = all.FirstOrDefault(x => x.Id == masterTemplateId);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()));
|
||||
foreach (var parent in descendants)
|
||||
{
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
|
||||
//return the list - it will be naturally ordered by level
|
||||
return descendants;
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<ITemplate> GetDescendants(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
var all = base.GetAll().ToArray();
|
||||
var descendants = new List<ITemplate>();
|
||||
if (alias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var path = Database.ExecuteScalar<string>(
|
||||
"SELECT umbracoNode.path FROM cmsTemplate INNER JOIN umbracoNode ON cmsTemplate.nodeId = umbracoNode.id WHERE cmsTemplate.alias = @alias", new { alias = alias });
|
||||
|
||||
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new {query = path + ",%" });
|
||||
var parent = all.FirstOrDefault(x => x.Alias == alias);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
//recursively add all children
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()));
|
||||
foreach (var parent in descendants)
|
||||
{
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
}
|
||||
//return the list - it will be naturally ordered by level
|
||||
return descendants;
|
||||
}
|
||||
|
||||
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
private void AddChildren(ITemplate[] all, List<ITemplate> descendants, string masterAlias)
|
||||
{
|
||||
var c = all.Where(x => x.MasterTemplateAlias == masterAlias).ToArray();
|
||||
descendants.AddRange(c);
|
||||
if (c.Any() == false) return;
|
||||
//recurse through all children
|
||||
foreach (var child in c)
|
||||
{
|
||||
AddChildren(all, descendants, child.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -610,7 +571,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public TemplateNode GetTemplateNode(string alias)
|
||||
{
|
||||
//first get all template objects
|
||||
var allTemplates = GetAll().ToArray();
|
||||
var allTemplates = base.GetAll().ToArray();
|
||||
|
||||
var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias == alias);
|
||||
if (selfTemplate == null)
|
||||
@@ -633,6 +594,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return FindTemplateInTree(topNode, alias);
|
||||
}
|
||||
|
||||
[Obsolete("Only used by obsolete code")]
|
||||
private static TemplateNode WalkTree(TemplateNode current, string alias)
|
||||
{
|
||||
//now walk the tree to find the node
|
||||
@@ -683,7 +645,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var engine = _templateConfig.DefaultRenderingEngine;
|
||||
var viewHelper = new ViewHelper(_viewsFileSystem);
|
||||
if (!viewHelper.ViewExists(template))
|
||||
if (viewHelper.ViewExists(template) == false)
|
||||
{
|
||||
if (template.Content.IsNullOrWhiteSpace() == false && MasterPageHelper.IsMasterPageSyntax(template.Content))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Umbraco.Core.Configuration;
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -18,6 +19,7 @@ namespace Umbraco.Core.Persistence
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly CacheHelper _noCache;
|
||||
private readonly IUmbracoSettingsSection _settings;
|
||||
|
||||
#region Ctors
|
||||
@@ -30,6 +32,29 @@ namespace Umbraco.Core.Persistence
|
||||
if (settings == null) throw new ArgumentNullException("settings");
|
||||
|
||||
_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;
|
||||
}));
|
||||
}
|
||||
|
||||
_noCache = CacheHelper.CreateDisabledCacheHelper();
|
||||
_logger = logger;
|
||||
_sqlSyntax = sqlSyntax;
|
||||
_settings = settings;
|
||||
@@ -53,6 +78,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
|
||||
_cacheHelper = cacheHelper;
|
||||
_noCache = CacheHelper.CreateDisabledCacheHelper();
|
||||
}
|
||||
|
||||
[Obsolete("Use the ctor specifying all dependencies instead")]
|
||||
@@ -80,15 +106,15 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public virtual ITaskRepository CreateTaskRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new TaskRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
return new TaskRepository(uow,
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual IAuditRepository CreateAuditRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new AuditRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -128,7 +154,6 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new DataTypeDefinitionRepository(
|
||||
uow,
|
||||
_cacheHelper,
|
||||
_cacheHelper,
|
||||
_logger, _sqlSyntax,
|
||||
CreateContentTypeRepository(uow));
|
||||
@@ -140,8 +165,7 @@ namespace Umbraco.Core.Persistence
|
||||
uow,
|
||||
_cacheHelper,
|
||||
_logger,
|
||||
_sqlSyntax,
|
||||
CreateLanguageRepository(uow));
|
||||
_sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual ILanguageRepository CreateLanguageRepository(IDatabaseUnitOfWork uow)
|
||||
@@ -175,7 +199,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new RelationRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax,
|
||||
CreateRelationTypeRepository(uow));
|
||||
}
|
||||
@@ -184,7 +208,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new RelationTypeRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -222,7 +246,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new MigrationEntryRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -283,8 +307,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new MemberGroupRepository(uow,
|
||||
_cacheHelper,
|
||||
_logger, _sqlSyntax,
|
||||
_cacheHelper);
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow)
|
||||
@@ -292,15 +315,15 @@ namespace Umbraco.Core.Persistence
|
||||
return new EntityRepository(uow);
|
||||
}
|
||||
|
||||
public IDomainRepository CreateDomainRepository(IDatabaseUnitOfWork uow)
|
||||
public virtual IDomainRepository CreateDomainRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new DomainRepository(uow, _cacheHelper, _logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public ITaskTypeRepository CreateTaskTypeRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new TaskTypeRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
return new TaskTypeRepository(uow,
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +357,9 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
//TODO: Do we need to do more here?? need to make sure that the forms cookie is gone, but is that
|
||||
// taken care of in our custom middleware somehow?
|
||||
ctx.Authentication.SignOut();
|
||||
ctx.Authentication.SignOut(
|
||||
Core.Constants.Security.BackOfficeAuthenticationType,
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Security;
|
||||
@@ -97,6 +99,8 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
_userService.Save(member);
|
||||
|
||||
if (member.Id == 0) throw new DataException("Could not create the user, check logs for details");
|
||||
|
||||
//re-assign id
|
||||
user.Id = member.Id;
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
//remove the cache now that it has changed SD: I'm leaving this here even though it
|
||||
// is taken care of by events as well, I think unit tests may rely on it being cleared here.
|
||||
_cache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
|
||||
_cache.RuntimeCache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1977,6 +1977,10 @@ namespace Umbraco.Core.Services
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
if (published == false)
|
||||
{
|
||||
content.ChangePublishedState(PublishedState.Saved);
|
||||
}
|
||||
//Since this is the Save and Publish method, the content should be saved even though the publish fails or isn't allowed
|
||||
if (content.HasIdentity == false)
|
||||
{
|
||||
@@ -2119,6 +2123,22 @@ namespace Umbraco.Core.Services
|
||||
content.Name, content.Id));
|
||||
return PublishStatusType.FailedPathNotPublished;
|
||||
}
|
||||
else if (content.ExpireDate.HasValue && content.ExpireDate.Value > DateTime.MinValue && DateTime.Now > content.ExpireDate.Value)
|
||||
{
|
||||
Logger.Info<ContentService>(
|
||||
string.Format(
|
||||
"Content '{0}' with Id '{1}' has expired and could not be published.",
|
||||
content.Name, content.Id));
|
||||
return PublishStatusType.FailedHasExpired;
|
||||
}
|
||||
else if (content.ReleaseDate.HasValue && content.ReleaseDate.Value > DateTime.MinValue && content.ReleaseDate.Value > DateTime.Now)
|
||||
{
|
||||
Logger.Info<ContentService>(
|
||||
string.Format(
|
||||
"Content '{0}' with Id '{1}' is awaiting release and could not be published.",
|
||||
content.Name, content.Id));
|
||||
return PublishStatusType.FailedAwaitingRelease;
|
||||
}
|
||||
|
||||
return PublishStatusType.Success;
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ namespace Umbraco.Core.Services
|
||||
new XElement("Type", propertyType.PropertyEditorAlias),
|
||||
new XElement("Definition", definition.Key),
|
||||
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
|
||||
new XElement("SortOrder", propertyType.SortOrder),
|
||||
new XElement("Mandatory", propertyType.Mandatory.ToString()),
|
||||
new XElement("Validation", propertyType.ValidationRegExp),
|
||||
new XElement("Description", new XCData(propertyType.Description)));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
@@ -371,6 +372,8 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TemplateNode GetTemplateNode(string alias)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
|
||||
@@ -385,6 +388,8 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="anyNode"></param>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
|
||||
|
||||
@@ -94,6 +94,9 @@ namespace Umbraco.Core.Services
|
||||
repository.AddOrUpdate(item);
|
||||
uow.Commit();
|
||||
|
||||
//ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(item);
|
||||
|
||||
SavedDictionaryItem.RaiseEvent(new SaveEventArgs<IDictionaryItem>(item), this);
|
||||
|
||||
return item;
|
||||
@@ -109,7 +112,10 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.Get(id);
|
||||
var item = repository.Get(id);
|
||||
//ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +128,10 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.Get(id);
|
||||
var item = repository.Get(id);
|
||||
//ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +144,10 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.Get(key);
|
||||
var item = repository.Get(key);
|
||||
//ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +161,9 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IDictionaryItem>.Builder.Where(x => x.ParentId == parentId);
|
||||
var items = repository.GetByQuery(query);
|
||||
|
||||
var items = repository.GetByQuery(query).ToArray();
|
||||
//ensure the lazy Language callback is assigned
|
||||
items.ForEach(EnsureDictionaryItemLanguageCallback);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +177,10 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.GetDictionaryItemDescendants(parentId);
|
||||
var items = repository.GetDictionaryItemDescendants(parentId).ToArray();
|
||||
//ensure the lazy Language callback is assigned
|
||||
items.ForEach(EnsureDictionaryItemLanguageCallback);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +193,9 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = RepositoryFactory.CreateDictionaryRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IDictionaryItem>.Builder.Where(x => x.ParentId == null);
|
||||
var items = repository.GetByQuery(query);
|
||||
|
||||
var items = repository.GetByQuery(query).ToArray();
|
||||
//ensure the lazy Language callback is assigned
|
||||
items.ForEach(EnsureDictionaryItemLanguageCallback);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -211,6 +228,8 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
repository.AddOrUpdate(dictionaryItem);
|
||||
uow.Commit();
|
||||
//ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(dictionaryItem);
|
||||
}
|
||||
|
||||
SavedDictionaryItem.RaiseEvent(new SaveEventArgs<IDictionaryItem>(dictionaryItem, false), this);
|
||||
@@ -265,10 +284,6 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = RepositoryFactory.CreateLanguageRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.GetByCultureName(cultureName);
|
||||
//var query = Query<ILanguage>.Builder.Where(x => x.CultureName == cultureName);
|
||||
//var items = repository.GetByQuery(query);
|
||||
|
||||
//return items.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,10 +297,6 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = RepositoryFactory.CreateLanguageRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.GetByIsoCode(isoCode);
|
||||
//var query = Query<ILanguage>.Builder.Where(x => x.IsoCode == isoCode);
|
||||
//var items = repository.GetByQuery(query);
|
||||
|
||||
//return items.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +368,24 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is here to take care of a hack - the DictionaryTranslation model contains an ILanguage reference which we don't want but
|
||||
/// we cannot remove it because it would be a large breaking change, so we need to make sure it's resolved lazily. This is because
|
||||
/// if developers have a lot of dictionary items and translations, the caching and cloning size gets much much larger because of
|
||||
/// the large object graphs. So now we don't cache or clone the attached ILanguage
|
||||
/// </summary>
|
||||
private void EnsureDictionaryItemLanguageCallback(IDictionaryItem d)
|
||||
{
|
||||
var item = d as DictionaryItem;
|
||||
if (item == null) return;
|
||||
|
||||
item.GetLanguage = GetLanguageById;
|
||||
foreach (var trans in item.Translations.OfType<DictionaryTranslation>())
|
||||
{
|
||||
trans.GetLanguage = GetLanguageById;
|
||||
}
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
|
||||
@@ -112,19 +112,15 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
|
||||
//convert all areas + keys to a single key with a '/'
|
||||
var areas = xmlSource[culture].Value.XPathSelectElements("//area");
|
||||
foreach (var area in areas)
|
||||
result = GetStoredTranslations(xmlSource, culture);
|
||||
|
||||
//merge with the english file in case there's keys in there that don't exist in the local file
|
||||
var englishCulture = new CultureInfo("en-US");
|
||||
if (culture.Equals(englishCulture) == false)
|
||||
{
|
||||
var keys = area.XPathSelectElements("./key");
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var dictionaryKey = string.Format("{0}/{1}", (string) area.Attribute("alias"), (string) key.Attribute("alias"));
|
||||
//there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files
|
||||
if (result.ContainsKey(dictionaryKey) == false)
|
||||
{
|
||||
result.Add(dictionaryKey, key.Value);
|
||||
}
|
||||
}
|
||||
var englishResults = GetStoredTranslations(xmlSource, englishCulture);
|
||||
foreach (var englishResult in englishResults.Where(englishResult => result.ContainsKey(englishResult.Key) == false))
|
||||
result.Add(englishResult.Key, englishResult.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -153,6 +149,25 @@ namespace Umbraco.Core.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetStoredTranslations(IDictionary<CultureInfo, Lazy<XDocument>> xmlSource, CultureInfo cult)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
var areas = xmlSource[cult].Value.XPathSelectElements("//area");
|
||||
foreach (var area in areas)
|
||||
{
|
||||
var keys = area.XPathSelectElements("./key");
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var dictionaryKey = string.Format("{0}/{1}", (string)area.Attribute("alias"),
|
||||
(string)key.Attribute("alias"));
|
||||
//there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files
|
||||
if (result.ContainsKey(dictionaryKey) == false)
|
||||
result.Add(dictionaryKey, key.Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all currently supported cultures
|
||||
/// </summary>
|
||||
@@ -254,23 +269,35 @@ namespace Umbraco.Core.Services
|
||||
return "[" + key + "]";
|
||||
}
|
||||
|
||||
var cultureSource = xmlSource[culture].Value;
|
||||
|
||||
var xpath = area.IsNullOrWhiteSpace()
|
||||
? string.Format("//key [@alias = '{0}']", key)
|
||||
: string.Format("//area [@alias = '{0}']/key [@alias = '{1}']", area, key);
|
||||
|
||||
var found = cultureSource.XPathSelectElement(xpath);
|
||||
var found = FindTranslation(xmlSource, culture, area, key);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
return ParseTokens(found.Value, tokens);
|
||||
}
|
||||
|
||||
// Fall back to English by default if we can't find the key
|
||||
found = FindTranslation(xmlSource, new CultureInfo("en-US"), area, key);
|
||||
if (found != null)
|
||||
return ParseTokens(found.Value, tokens);
|
||||
|
||||
//NOTE: Based on how legacy works, the default text does not contain the area, just the key
|
||||
// If it can't be found in either file, fall back to the default, showing just the key in square brackets
|
||||
// NOTE: Based on how legacy works, the default text does not contain the area, just the key
|
||||
return "[" + key + "]";
|
||||
}
|
||||
|
||||
private XElement FindTranslation(IDictionary<CultureInfo, Lazy<XDocument>> xmlSource, CultureInfo culture, string area, string key)
|
||||
{
|
||||
var cultureSource = xmlSource[culture].Value;
|
||||
|
||||
var xpath = area.IsNullOrWhiteSpace()
|
||||
? string.Format("//key [@alias = '{0}']", key)
|
||||
: string.Format("//area [@alias = '{0}']/key [@alias = '{1}']", area, key);
|
||||
|
||||
var found = cultureSource.XPathSelectElement(xpath);
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the tokens in the value
|
||||
/// </summary>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Dictionary;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for ILocalizedTextService
|
||||
/// </summary>
|
||||
@@ -61,5 +61,43 @@ namespace Umbraco.Core.Services
|
||||
return variables.Select((s, i) => new { index = i.ToString(CultureInfo.InvariantCulture), value = s })
|
||||
.ToDictionary(keyvals => keyvals.index, keyvals => keyvals.value);
|
||||
}
|
||||
|
||||
private static ICultureDictionary _cultureDictionary;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: We need to refactor how we work with ICultureDictionary - this is supposed to be the 'fast' way to
|
||||
/// do readonly access to the Dictionary without using the ILocalizationService. See TODO Notes in `DefaultCultureDictionary`
|
||||
/// Also NOTE that the ICultureDictionary is based on the ILocalizationService not the ILocalizedTextService (which is used
|
||||
/// only for the localization files - not the dictionary)
|
||||
/// </summary>
|
||||
/// <param name="manager"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
internal static string UmbracoDictionaryTranslate(this ILocalizedTextService manager, string text)
|
||||
{
|
||||
var cultureDictionary = CultureDictionary;
|
||||
return UmbracoDictionaryTranslate(text, cultureDictionary);
|
||||
}
|
||||
|
||||
private static string UmbracoDictionaryTranslate(string text, ICultureDictionary cultureDictionary)
|
||||
{
|
||||
if (text == null)
|
||||
return null;
|
||||
|
||||
if (text.StartsWith("#") == false)
|
||||
return text;
|
||||
|
||||
text = text.Substring(1);
|
||||
return cultureDictionary[text].IfNullOrWhiteSpace(text);
|
||||
}
|
||||
|
||||
private static ICultureDictionary CultureDictionary
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cultureDictionary
|
||||
?? (_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
member.RawPasswordValue = result.RawPasswordValue;
|
||||
member.LastPasswordChangeDate = result.LastPasswordChangeDate;
|
||||
member.UpdateDate = member.UpdateDate;
|
||||
member.UpdateDate = result.UpdateDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -652,12 +652,17 @@ namespace Umbraco.Core.Services
|
||||
if (dataTypeDefinition == null) continue;
|
||||
}
|
||||
|
||||
var sortOrder = 0;
|
||||
var sortOrderElement = property.Element("SortOrder");
|
||||
if (sortOrderElement != null)
|
||||
int.TryParse(sortOrderElement.Value, out sortOrder);
|
||||
var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value)
|
||||
{
|
||||
Name = property.Element("Name").Value,
|
||||
Description = property.Element("Description").Value,
|
||||
Mandatory = property.Element("Mandatory").Value.ToLowerInvariant().Equals("true"),
|
||||
ValidationRegExp = property.Element("Validation").Value,
|
||||
SortOrder = sortOrder
|
||||
};
|
||||
|
||||
var tab = property.Element("Tab").Value;
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
//remove the cache so it gets re-read ... SD: I'm leaving this here even though it
|
||||
// is taken care of by events as well, I think unit tests may rely on it being cleared here.
|
||||
_cache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
|
||||
_cache.RuntimeCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ namespace Umbraco.Core.Services
|
||||
_entityService = new Lazy<IEntityService>(() => new EntityService(
|
||||
provider, repositoryFactory, logger, eventMessagesFactory,
|
||||
_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _memberService.Value, _memberTypeService.Value,
|
||||
//TODO: Consider making this an isolated cache instead of using the global one
|
||||
cache.RuntimeCache));
|
||||
|
||||
if (_relationService == null)
|
||||
|
||||
@@ -266,7 +266,7 @@ namespace Umbraco.Core.Services
|
||||
//should never be null but it could have been deleted by another thread.
|
||||
user.RawPasswordValue = result.RawPasswordValue;
|
||||
user.LastPasswordChangeDate = result.LastPasswordChangeDate;
|
||||
user.UpdateDate = user.UpdateDate;
|
||||
user.UpdateDate = result.UpdateDate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
@@ -277,12 +278,42 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove old instructions from the database.
|
||||
/// 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
|
||||
/// 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>
|
||||
private void PruneOldInstructions()
|
||||
{
|
||||
_appContext.DatabaseContext.Database.Delete<CacheInstructionDto>("WHERE utcStamp < @pruneDate",
|
||||
new { pruneDate = DateTime.UtcNow.AddDays(-_options.DaysToRetainInstructions) });
|
||||
var pruneDate = DateTime.UtcNow.AddDays(-_options.DaysToRetainInstructions);
|
||||
var sqlSyntax = _appContext.DatabaseContext.SqlSyntax;
|
||||
|
||||
//NOTE: this query could work on SQL server and MySQL:
|
||||
/*
|
||||
SELECT id
|
||||
FROM umbracoCacheInstruction
|
||||
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
|
||||
// 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.
|
||||
|
||||
var deleteQuery = new Sql().Select("cacheIns.id")
|
||||
.From("umbracoCacheInstruction cacheIns")
|
||||
.InnerJoin("(SELECT MAX(id) id FROM umbracoCacheInstruction) tMax")
|
||||
.On("cacheIns.id <> tMax.id")
|
||||
.Where("cacheIns.utcStamp < @pruneDate", new {pruneDate = pruneDate});
|
||||
|
||||
var deleteSql = sqlSyntax.GetDeleteSubquery(
|
||||
"umbracoCacheInstruction",
|
||||
"id",
|
||||
deleteQuery);
|
||||
|
||||
_appContext.DatabaseContext.Database.Execute(deleteSql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,8 +329,9 @@ namespace Umbraco.Core.Sync
|
||||
.Where<CacheInstructionDto>(dto => dto.Id == _lastId);
|
||||
|
||||
var dtos = _appContext.DatabaseContext.Database.Fetch<CacheInstructionDto>(sql);
|
||||
|
||||
if (dtos.Count == 0)
|
||||
_lastId = -1;
|
||||
_lastId = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,4 +501,5 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,19 +156,29 @@
|
||||
<Compile Include="CacheHelper.cs" />
|
||||
<Compile Include="Cache\CacheKeys.cs" />
|
||||
<Compile Include="Cache\CacheProviderExtensions.cs" />
|
||||
<Compile Include="Cache\DefaultRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\DefaultRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\FullDataSetRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\FullDataSetRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\ICacheProvider.cs" />
|
||||
<Compile Include="Cache\CacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\CacheRefresherEventArgs.cs" />
|
||||
<Compile Include="Cache\DictionaryCacheProviderBase.cs" />
|
||||
<Compile Include="Cache\HttpRequestCacheProvider.cs" />
|
||||
<Compile Include="Cache\IRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\IPayloadCacheRefresher.cs" />
|
||||
<Compile Include="Cache\IRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\IsolatedRuntimeCache.cs" />
|
||||
<Compile Include="Cache\ObjectCacheRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\IRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\HttpRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\IJsonCacheRefresher.cs" />
|
||||
<Compile Include="Cache\JsonCacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\NullCacheProvider.cs" />
|
||||
<Compile Include="Cache\SingleItemsOnlyRepositoryCachePolicy.cs" />
|
||||
<Compile Include="Cache\OnlySingleItemsRepositoryCachePolicyFactory.cs" />
|
||||
<Compile Include="Cache\PayloadCacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\RepositoryCachePolicyOptions.cs" />
|
||||
<Compile Include="Cache\StaticCacheProvider.cs" />
|
||||
<Compile Include="Cache\TypedCacheRefresherBase.cs" />
|
||||
<Compile Include="CodeAnnotations\FriendlyNameAttribute.cs" />
|
||||
@@ -176,6 +186,7 @@
|
||||
<Compile Include="CodeAnnotations\UmbracoWillObsoleteAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
|
||||
<Compile Include="Collections\DeepCloneableList.cs" />
|
||||
<Compile Include="ConcurrentHashSet.cs" />
|
||||
<Compile Include="Configuration\BaseRest\IBaseRestSection.cs" />
|
||||
<Compile Include="Configuration\BaseRest\IExtension.cs" />
|
||||
@@ -441,7 +452,7 @@
|
||||
<Compile Include="Persistence\RecordPersistenceType.cs" />
|
||||
<Compile Include="Persistence\Relators\AccessRulesRelator.cs" />
|
||||
<Compile Include="Persistence\Repositories\AuditRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\DeepCloneRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\DeepCloneRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\Interfaces\IAuditRepository.cs" />
|
||||
@@ -455,7 +466,6 @@
|
||||
<Compile Include="Persistence\Repositories\Interfaces\ITaskTypeRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\MigrationEntryRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\PublicAccessRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\RepositoryCacheOptions.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\TaskTypeRepository.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\GridValueConverter.cs" />
|
||||
|
||||
@@ -4,9 +4,11 @@ using System.Web;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Tests.Collections;
|
||||
|
||||
namespace Umbraco.Tests.Cache
|
||||
{
|
||||
@@ -36,6 +38,23 @@ namespace Umbraco.Tests.Cache
|
||||
get { return _provider; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clones_List()
|
||||
{
|
||||
var original = new DeepCloneableList<DeepCloneableListTests.TestClone>();
|
||||
original.Add(new DeepCloneableListTests.TestClone());
|
||||
original.Add(new DeepCloneableListTests.TestClone());
|
||||
original.Add(new DeepCloneableListTests.TestClone());
|
||||
|
||||
var val = _provider.GetCacheItem<DeepCloneableList<DeepCloneableListTests.TestClone>>("test", () => original);
|
||||
|
||||
Assert.AreEqual(original.Count, val.Count);
|
||||
foreach (var item in val)
|
||||
{
|
||||
Assert.IsTrue(item.IsClone);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensures_Cloned_And_Reset()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Caching;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Cache
|
||||
{
|
||||
[TestFixture]
|
||||
public class DefaultCachePolicyTests
|
||||
{
|
||||
[Test]
|
||||
public void Caches_Single()
|
||||
{
|
||||
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 DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
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 cache = new Mock<IRuntimeCacheProvider>();
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => (AuditItem) null);
|
||||
Assert.IsNotNull(found);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Caches_Per_Id_For_Get_All()
|
||||
{
|
||||
var cached = new List<string>();
|
||||
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);
|
||||
});
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] {});
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] {}, o => new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, cached.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_All_Without_Ids_From_Cache()
|
||||
{
|
||||
var cache = new Mock<IRuntimeCacheProvider>();
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
|
||||
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] {}, o => new[] {(AuditItem) null});
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void If_CreateOrUpdate_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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Caching;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Cache
|
||||
{
|
||||
[TestFixture]
|
||||
public class FullDataSetCachePolicyTests
|
||||
{
|
||||
[Test]
|
||||
public void Get_All_Caches_As_Single_List()
|
||||
{
|
||||
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.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, cached.Count);
|
||||
Assert.IsNotNull(list);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_All_Without_Ids_From_Cache()
|
||||
{
|
||||
var cache = new Mock<IRuntimeCacheProvider>();
|
||||
|
||||
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(() => new DeepCloneableList<AuditItem>
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
|
||||
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object);
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => new[] { (AuditItem)null });
|
||||
Assert.AreEqual(2, found.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Caching;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Cache
|
||||
{
|
||||
[TestFixture]
|
||||
public class SingleItemsOnlyCachePolicyTests
|
||||
{
|
||||
[Test]
|
||||
public void Get_All_Doesnt_Cache()
|
||||
{
|
||||
var cached = new List<string>();
|
||||
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);
|
||||
});
|
||||
cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny<string>())).Returns(new AuditItem[] { });
|
||||
|
||||
var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.GetAll(new object[] { }, o => new[]
|
||||
{
|
||||
new AuditItem(1, "blah", AuditType.Copy, 123),
|
||||
new AuditItem(2, "blah2", AuditType.Copy, 123)
|
||||
});
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, cached.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Caches_Single()
|
||||
{
|
||||
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 SingleItemsOnlyRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
|
||||
using (defaultPolicy)
|
||||
{
|
||||
var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123));
|
||||
}
|
||||
Assert.IsTrue(isCached);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public class DeepCloneableListTests
|
||||
{
|
||||
[Test]
|
||||
public void Deep_Clones_All_Elements()
|
||||
{
|
||||
var list = new DeepCloneableList<TestClone>();
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
|
||||
var cloned = list.DeepClone() as DeepCloneableList<TestClone>;
|
||||
|
||||
Assert.IsNotNull(cloned);
|
||||
Assert.AreNotSame(list, cloned);
|
||||
Assert.AreEqual(list.Count, cloned.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clones_Each_Item()
|
||||
{
|
||||
var list = new DeepCloneableList<TestClone>();
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
|
||||
var cloned = (DeepCloneableList<TestClone>) list.DeepClone();
|
||||
|
||||
foreach (var item in cloned)
|
||||
{
|
||||
Assert.IsTrue(item.IsClone);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cloned_Sequence_Equals()
|
||||
{
|
||||
var list = new DeepCloneableList<TestClone>();
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
list.Add(new TestClone());
|
||||
|
||||
var cloned = (DeepCloneableList<TestClone>)list.DeepClone();
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestClone : IDeepCloneable, IEquatable<TestClone>
|
||||
{
|
||||
public TestClone(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
IsClone = true;
|
||||
}
|
||||
|
||||
public TestClone()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
public bool IsClone { get; private set; }
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
return new TestClone(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current object is equal to another object of the same type.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
public bool Equals(TestClone other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Id.Equals(other.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the specified object is equal to the current object; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="obj">The object to compare with the current object. </param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((TestClone)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for the current object.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(TestClone left, TestClone right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(TestClone left, TestClone right)
|
||||
{
|
||||
return Equals(left, right) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +223,41 @@ namespace Umbraco.Tests.Macros
|
||||
<p> </p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Format_RTE_Data_For_Persistence_Multiline_Parameters()
|
||||
{
|
||||
var content = @"<html>
|
||||
<body>
|
||||
<h1>asdfasdf</h1>
|
||||
<div class='umb-macro-holder mceNonEditable' att1='asdf' att2='asdfasdfasdf' att3=""sdfsdfd"">
|
||||
<!-- <?UMBRACO_MACRO macroAlias=""myMacro"" param1=""test1"" param2=""test2
|
||||
dfdsfds"" /> -->
|
||||
asdfasdf
|
||||
asdfas
|
||||
<span>asdfasdfasdf</span>
|
||||
<p>asdfasdf</p>
|
||||
</div>
|
||||
<span>asdfdasf</span>
|
||||
<div>
|
||||
asdfsdf
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
var result = MacroTagParser.FormatRichTextContentForPersistence(content);
|
||||
|
||||
Assert.AreEqual(@"<html>
|
||||
<body>
|
||||
<h1>asdfasdf</h1>
|
||||
<?UMBRACO_MACRO macroAlias=""myMacro"" param1=""test1"" param2=""test2
|
||||
dfdsfds"" />
|
||||
<span>asdfdasf</span>
|
||||
<div>
|
||||
asdfsdf
|
||||
</div>
|
||||
</body>
|
||||
</html>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag()
|
||||
{
|
||||
@@ -269,6 +304,28 @@ namespace Umbraco.Tests.Macros
|
||||
<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Format_RTE_Data_For_Editor_With_Multiline_Parameters()
|
||||
{
|
||||
|
||||
var content = @"<p>asdfasdf</p>
|
||||
<p>asdfsadf</p>
|
||||
<?UMBRACO_MACRO macroAlias=""Map"" test1=""value1"" test2=""value2
|
||||
test"" />
|
||||
<p>asdfasdf</p>";
|
||||
var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary<string, string>() { { "test1", "value1" }, { "test2", "value2\r\ntest" } });
|
||||
|
||||
Assert.AreEqual(@"<p>asdfasdf</p>
|
||||
<p>asdfsadf</p>
|
||||
<div class=""umb-macro-holder mceNonEditable"" test1=""value1"" test2=""value2
|
||||
test"">
|
||||
<!-- <?UMBRACO_MACRO macroAlias=""Map"" test1=""value1"" test2=""value2
|
||||
test"" /> -->
|
||||
<ins>Macro alias: <strong>Map</strong></ins></div>
|
||||
<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Format_RTE_Data_For_Persistence()
|
||||
{
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace Umbraco.Tests.Macros
|
||||
var cacheHelper = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
new NullCacheProvider());
|
||||
new NullCacheProvider(),
|
||||
new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider()));
|
||||
ApplicationContext.Current = new ApplicationContext(cacheHelper, new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
|
||||
UmbracoConfig.For.SetUmbracoSettings(SettingsForTests.GetDefault());
|
||||
@@ -36,7 +37,7 @@ namespace Umbraco.Tests.Macros
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.ClearAllCache();
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearAllCache();
|
||||
ApplicationContext.Current.DisposeIfDisposable();
|
||||
ApplicationContext.Current = null;
|
||||
}
|
||||
@@ -136,11 +137,11 @@ namespace Umbraco.Tests.Macros
|
||||
public void Macro_Needs_Removing_Based_On_Macro_File(int minutesToNow, bool expectedResult)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
ApplicationContext.Current.ApplicationCache.InsertCacheItem(
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.InsertCacheItem(
|
||||
"TestDate",
|
||||
CacheItemPriority.NotRemovable,
|
||||
new TimeSpan(0, 0, 60),
|
||||
() => now.AddMinutes(minutesToNow)); //add a datetime value of 'now' with the minutes offset
|
||||
priority: CacheItemPriority.NotRemovable,
|
||||
timeout: new TimeSpan(0, 0, 60),
|
||||
getCacheItem: () => now.AddMinutes(minutesToNow)); //add a datetime value of 'now' with the minutes offset
|
||||
|
||||
//now we need to update a file's date to 'now' to compare
|
||||
var path = Path.Combine(TestHelpers.TestHelper.CurrentAssemblyDirectory, "temp.txt");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Serialization;
|
||||
@@ -36,12 +37,15 @@ namespace Umbraco.Tests.Models
|
||||
Assert.AreEqual(clone.Key, item.Key);
|
||||
Assert.AreEqual(clone.UpdateDate, item.UpdateDate);
|
||||
Assert.AreNotSame(clone.Language, item.Language);
|
||||
Assert.AreEqual(clone.Language, item.Language);
|
||||
//This is null because we are ignoring it from cloning due to caching/cloning issues - we don't really want
|
||||
// this entity attached to this item but we're stuck with it for now
|
||||
Assert.IsNull(clone.Language);
|
||||
Assert.AreEqual(clone.LanguageId, item.LanguageId);
|
||||
Assert.AreEqual(clone.Value, item.Value);
|
||||
|
||||
//This double verifies by reflection
|
||||
var allProps = clone.GetType().GetProperties();
|
||||
foreach (var propertyInfo in allProps)
|
||||
foreach (var propertyInfo in allProps.Where(x => x.Name != "Language"))
|
||||
{
|
||||
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null));
|
||||
}
|
||||
|
||||
@@ -57,6 +57,58 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return repository;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Rebuild_Xml_Structures_With_Non_Latest_Version()
|
||||
{
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
ContentTypeRepository contentTypeRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
|
||||
{
|
||||
var contentType1 = MockedContentTypes.CreateSimpleContentType("Textpage1", "Textpage1");
|
||||
contentTypeRepository.AddOrUpdate(contentType1);
|
||||
|
||||
var allCreated = new List<IContent>();
|
||||
|
||||
//create 100 non published
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var c1 = MockedContent.CreateSimpleContent(contentType1);
|
||||
repository.AddOrUpdate(c1);
|
||||
allCreated.Add(c1);
|
||||
}
|
||||
//create 100 published
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var c1 = MockedContent.CreateSimpleContent(contentType1);
|
||||
c1.ChangePublishedState(PublishedState.Published);
|
||||
repository.AddOrUpdate(c1);
|
||||
allCreated.Add(c1);
|
||||
}
|
||||
unitOfWork.Commit();
|
||||
|
||||
//now create some versions of this content - this shouldn't affect the xml structures saved
|
||||
for (int i = 0; i < allCreated.Count; i++)
|
||||
{
|
||||
allCreated[i].Name = "blah" + i;
|
||||
//IMPORTANT testing note here: We need to changed the published state here so that
|
||||
// it doesn't automatically think this is simply publishing again - this forces the latest
|
||||
// version to be Saved and not published
|
||||
allCreated[i].ChangePublishedState(PublishedState.Saved);
|
||||
repository.AddOrUpdate(allCreated[i]);
|
||||
}
|
||||
unitOfWork.Commit();
|
||||
|
||||
//delete all xml
|
||||
unitOfWork.Database.Execute("DELETE FROM cmsContentXml");
|
||||
Assert.AreEqual(0, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
|
||||
|
||||
repository.RebuildXmlStructures(media => new XElement("test"), 10);
|
||||
|
||||
Assert.AreEqual(100, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Rebuild_All_Xml_Structures()
|
||||
{
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
var dataTypeDefinitionRepository = new DataTypeDefinitionRepository(
|
||||
unitOfWork, CacheHelper.CreateDisabledCacheHelper(),
|
||||
CacheHelper.CreateDisabledCacheHelper(),
|
||||
Mock.Of<ILogger>(), SqlSyntax,
|
||||
new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax,
|
||||
new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>())));
|
||||
@@ -376,10 +375,14 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
|
||||
var cache = new CacheHelper(new ObjectCacheRuntimeCacheProvider(), new StaticCacheProvider(), new StaticCacheProvider());
|
||||
var cache = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider()));
|
||||
|
||||
Func<DataTypeDefinitionRepository> creator = () => new DataTypeDefinitionRepository(
|
||||
unitOfWork, CacheHelper.CreateDisabledCacheHelper(),
|
||||
unitOfWork,
|
||||
cache,
|
||||
Mock.Of<ILogger>(), SqlSyntax,
|
||||
new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax,
|
||||
@@ -402,7 +405,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var collection = repository.GetPreValuesCollectionByDataTypeId(dtd.Id);
|
||||
}
|
||||
|
||||
var cached = cache.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
var cached = cache.IsolatedRuntimeCache.GetCache<IDataTypeDefinition>().Result
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
|
||||
Assert.IsNotNull(cached);
|
||||
Assert.AreEqual(1, cached.Count());
|
||||
@@ -415,10 +419,14 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
|
||||
var cache = new CacheHelper(new ObjectCacheRuntimeCacheProvider(), new StaticCacheProvider(), new StaticCacheProvider());
|
||||
var cache = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider()));
|
||||
|
||||
Func<DataTypeDefinitionRepository> creator = () => new DataTypeDefinitionRepository(
|
||||
unitOfWork, CacheHelper.CreateDisabledCacheHelper(),
|
||||
unitOfWork,
|
||||
cache,
|
||||
Mock.Of<ILogger>(), SqlSyntax,
|
||||
new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax,
|
||||
@@ -441,7 +449,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var val = repository.GetPreValueAsString(Convert.ToInt32(id));
|
||||
}
|
||||
|
||||
var cached = cache.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
var cached = cache.IsolatedRuntimeCache.GetCache<IDataTypeDefinition>().Result
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
|
||||
Assert.IsNotNull(cached);
|
||||
Assert.AreEqual(1, cached.Count());
|
||||
|
||||
@@ -27,10 +27,9 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
CreateTestData();
|
||||
}
|
||||
|
||||
private DictionaryRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out LanguageRepository languageRepository)
|
||||
private DictionaryRepository CreateRepository(IDatabaseUnitOfWork unitOfWork)
|
||||
{
|
||||
languageRepository = new LanguageRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax);
|
||||
var dictionaryRepository = new DictionaryRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), new SqlCeSyntaxProvider(), languageRepository);
|
||||
var dictionaryRepository = new DictionaryRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), new SqlCeSyntaxProvider());
|
||||
return dictionaryRepository;
|
||||
}
|
||||
|
||||
@@ -41,8 +40,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
var dictionaryItem = (IDictionaryItem)new DictionaryItem("Testing1235")
|
||||
{
|
||||
@@ -74,8 +73,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
var dictionaryItem = (IDictionaryItem)new DictionaryItem("Testing1235")
|
||||
{
|
||||
@@ -107,8 +106,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
var dictionaryItem = (IDictionaryItem)new DictionaryItem("Testing1235")
|
||||
{
|
||||
@@ -141,8 +140,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
var dictionaryItem = (IDictionaryItem) new DictionaryItem("Testing1235");
|
||||
|
||||
@@ -168,8 +167,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -190,8 +188,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -211,8 +208,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -232,8 +228,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -251,8 +246,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var languageRepository = new LanguageRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
var language = languageRepository.Get(1);
|
||||
@@ -282,8 +277,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -310,8 +304,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var languageRepository = new LanguageRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax);
|
||||
var repository = new DictionaryRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), new SqlCeSyntaxProvider(), languageRepository);
|
||||
var repository = new DictionaryRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), new SqlCeSyntaxProvider());
|
||||
|
||||
var languageNo = new Language("nb-NO") { CultureName = "nb-NO" };
|
||||
ServiceContext.LocalizationService.Save(languageNo);
|
||||
@@ -325,12 +318,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
repository.AddOrUpdate(item);
|
||||
unitOfWork.Commit();
|
||||
|
||||
var dictionaryItem = repository.Get(1);
|
||||
|
||||
var dictionaryItem = (DictionaryItem)repository.Get(1);
|
||||
|
||||
// Assert
|
||||
Assert.That(dictionaryItem, Is.Not.Null);
|
||||
Assert.That(dictionaryItem.Translations.Count(), Is.EqualTo(3));
|
||||
Assert.That(dictionaryItem.Translations.Single(t => t.Language.IsoCode == "nb-NO").Value, Is.EqualTo("Les mer"));
|
||||
Assert.That(dictionaryItem.Translations.Single(t => t.LanguageId == languageNo.Id).Value, Is.EqualTo("Les mer"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -339,8 +332,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
@@ -361,8 +353,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
LanguageRepository languageRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out languageRepository))
|
||||
using (var repository = CreateRepository(unitOfWork))
|
||||
{
|
||||
|
||||
// Act
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
private MemberRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out MemberTypeRepository memberTypeRepository, out MemberGroupRepository memberGroupRepository)
|
||||
{
|
||||
memberTypeRepository = new MemberTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax);
|
||||
memberGroupRepository = new MemberGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax, CacheHelper.CreateDisabledCacheHelper());
|
||||
memberGroupRepository = new MemberGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax);
|
||||
var tagRepo = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax);
|
||||
var repository = new MemberRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of<IContentSection>());
|
||||
return repository;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
@@ -11,6 +12,13 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[TestFixture]
|
||||
public class PropertyEditorValueEditorTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
//normalize culture
|
||||
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
|
||||
}
|
||||
|
||||
[TestCase("{prop1: 'val1', prop2: 'val2'}", true)]
|
||||
[TestCase("{1,2,3,4}", false)]
|
||||
[TestCase("[1,2,3,4]", true)]
|
||||
|
||||
@@ -74,6 +74,31 @@ namespace Umbraco.Tests.Services.Importing
|
||||
Assert.That(uBlogsyLanding.CompositionPropertyGroups.Count(), Is.EqualTo(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PackagingService_Can_Import_Inherited_ContentTypes_And_Verify_PropertyTypes_UniqueIds()
|
||||
{
|
||||
// Arrange
|
||||
string strXml = ImportResources.InheritedDocTypes_Package;
|
||||
var xml = XElement.Parse(strXml);
|
||||
var dataTypeElement = xml.Descendants("DataTypes").First();
|
||||
var templateElement = xml.Descendants("Templates").First();
|
||||
var docTypeElement = xml.Descendants("DocumentTypes").First();
|
||||
var packagingService = ServiceContext.PackagingService;
|
||||
|
||||
// Act
|
||||
var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement);
|
||||
var templates = packagingService.ImportTemplates(templateElement);
|
||||
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
|
||||
|
||||
// Assert
|
||||
var mRBasePage = contentTypes.First(x => x.Alias == "MRBasePage");
|
||||
foreach (var propertyType in mRBasePage.PropertyTypes)
|
||||
{
|
||||
var propertyTypeDto = this.DatabaseContext.Database.First<PropertyTypeDto>("WHERE id = @id", new { id = propertyType.Id });
|
||||
Assert.AreEqual(propertyTypeDto.UniqueId, propertyType.Key);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PackagingService_Can_Import_Inherited_ContentTypes_And_Verify_PropertyGroups_And_PropertyTypes()
|
||||
{
|
||||
|
||||
@@ -175,6 +175,10 @@
|
||||
<Compile Include="AttemptTests.cs" />
|
||||
<Compile Include="Cache\CacheRefresherTests.cs" />
|
||||
<Compile Include="Cache\DeepCloneRuntimeCacheProviderTests.cs" />
|
||||
<Compile Include="Cache\DefaultCachePolicyTests.cs" />
|
||||
<Compile Include="Cache\FullDataSetCachePolicyTests.cs" />
|
||||
<Compile Include="Cache\SingleItemsOnlyCachePolicyTests.cs" />
|
||||
<Compile Include="Collections\DeepCloneableListTests.cs" />
|
||||
<Compile Include="Controllers\BackOfficeControllerUnitTests.cs" />
|
||||
<Compile Include="DelegateExtensionsTests.cs" />
|
||||
<Compile Include="Logging\AsyncRollingFileAppenderTest.cs" />
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Umbraco.Tests
|
||||
public void GetAbsolutePathDecoded(string input, string expected)
|
||||
{
|
||||
var source = new Uri(input, UriKind.RelativeOrAbsolute);
|
||||
var output = source.GetSafeAbsolutePathDecoded();
|
||||
var output = source.GetAbsolutePathDecoded();
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/* set default selection color */
|
||||
.slider-track .slider-selection {
|
||||
background: #89cdef;
|
||||
}
|
||||
|
||||
/* custom styling for triangle */
|
||||
.slider.slider-horizontal .slider-tick.triangle,
|
||||
.slider.slider-horizontal .slider-handle.triangle {
|
||||
box-shadow: none;
|
||||
border-width: 0 12px 15px 12px !important;
|
||||
margin-left: -12px;
|
||||
margin-top: -5px !important;
|
||||
}
|
||||
|
||||
.slider.slider-vertical .slider-tick.triangle,
|
||||
.slider.slider-vertical .slider-handle.triangle {
|
||||
box-shadow: none;
|
||||
border-width: 12px 0 12px 15px !important;
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
/* for custom handle remove box-shadow */
|
||||
.slider-track .slider-handle.custom {
|
||||
box-shadow: none;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
/* for custom handle add text-shadow */
|
||||
.slider-track .slider-handle.custom::before {
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
color: #337ab7;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
/* we make each tick a bit more clear */
|
||||
.slider .slider-tick {
|
||||
background-image: -webkit-linear-gradient(top, #e6e6e6 0, #dedede 100%);
|
||||
background-image: -o-linear-gradient(top, #e6e6e6 0, #dedede 100%);
|
||||
background-image: linear-gradient(to bottom, #e6e6e6 0, #dedede 100%);
|
||||
}
|
||||
|
||||
.slider .slider-tick.in-selection {
|
||||
background-image: -webkit-linear-gradient(top, #89cdef 0, #81bfde 100%);
|
||||
background-image: -o-linear-gradient(top, #89cdef 0, #81bfde 100%);
|
||||
background-image: linear-gradient(to bottom, #89cdef 0, #81bfde 100%);
|
||||
}
|
||||
|
||||
/* horizontal - triangle border-bottom color */
|
||||
.slider.slider-horizontal .slider-tick.triangle {
|
||||
border-bottom-color: #dedede;
|
||||
}
|
||||
.slider.slider-horizontal .slider-handle.triangle {
|
||||
border-bottom-color: #0480be;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick.triangle.in-selection {
|
||||
border-bottom-color: #81bfde;
|
||||
}
|
||||
|
||||
/* vertical - triangle border-left color */
|
||||
.slider.slider-vertical .slider-tick.triangle {
|
||||
border-left-color: #dedede;
|
||||
}
|
||||
.slider.slider-vertical .slider-handle.triangle {
|
||||
border-left-color: #0480be;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick.triangle.in-selection {
|
||||
border-left-color: #81bfde;
|
||||
}
|
||||
|
||||
.slider.slider-horizontal {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.slider.slider-horizontal {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*! =======================================================
|
||||
VERSION 5.2.6
|
||||
========================================================= */
|
||||
/*! =========================================================
|
||||
* bootstrap-slider.js
|
||||
*
|
||||
* Maintainers:
|
||||
* Kyle Kemp
|
||||
* - Twitter: @seiyria
|
||||
* - Github: seiyria
|
||||
* Rohit Kalkur
|
||||
* - Twitter: @Rovolutionary
|
||||
* - Github: rovolution
|
||||
*
|
||||
* =========================================================
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ========================================================= */
|
||||
.slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
.slider.slider-horizontal {
|
||||
width: 210px;
|
||||
height: 20px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-track {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
margin-top: -5px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-selection,
|
||||
.slider.slider-horizontal .slider-track-low,
|
||||
.slider.slider-horizontal .slider-track-high {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick,
|
||||
.slider.slider-horizontal .slider-handle {
|
||||
margin-left: -10px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick.triangle,
|
||||
.slider.slider-horizontal .slider-handle.triangle {
|
||||
border-width: 0 10px 10px 10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-bottom-color: #0480be;
|
||||
margin-top: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-tick-label-container .slider-tick-label {
|
||||
padding-top: 4px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
.slider.slider-vertical {
|
||||
height: 210px;
|
||||
width: 20px;
|
||||
}
|
||||
.slider.slider-vertical .slider-track {
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
margin-left: -5px;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-selection {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-track-low,
|
||||
.slider.slider-vertical .slider-track-high {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick,
|
||||
.slider.slider-vertical .slider-handle {
|
||||
margin-left: -5px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick.triangle,
|
||||
.slider.slider-vertical .slider-handle.triangle {
|
||||
border-width: 10px 0 10px 10px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border-left-color: #0480be;
|
||||
margin-left: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick-label-container {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.slider.slider-vertical .slider-tick-label-container .slider-tick-label {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.slider.slider-disabled .slider-handle {
|
||||
background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
|
||||
background-image: -o-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
|
||||
background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0);
|
||||
}
|
||||
.slider.slider-disabled .slider-track {
|
||||
background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-image: -o-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.slider input {
|
||||
display: none;
|
||||
}
|
||||
.slider .tooltip.top {
|
||||
margin-top: -36px;
|
||||
}
|
||||
.slider .tooltip-inner {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.slider .hide {
|
||||
display: none;
|
||||
}
|
||||
.slider-track {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-selection {
|
||||
position: absolute;
|
||||
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-selection.tick-slider-selection {
|
||||
background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%);
|
||||
background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%);
|
||||
background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0);
|
||||
}
|
||||
.slider-track-low,
|
||||
.slider-track-high {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-handle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #337ab7;
|
||||
background-image: -webkit-linear-gradient(top, #149bdf 0%, #0480be 100%);
|
||||
background-image: -o-linear-gradient(top, #149bdf 0%, #0480be 100%);
|
||||
background-image: linear-gradient(to bottom, #149bdf 0%, #0480be 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
|
||||
filter: none;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
.slider-handle.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.slider-handle.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-handle.custom {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-handle.custom::before {
|
||||
line-height: 20px;
|
||||
font-size: 20px;
|
||||
content: '\2605';
|
||||
color: #726204;
|
||||
}
|
||||
.slider-tick {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
filter: none;
|
||||
opacity: 0.8;
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
.slider-tick.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.slider-tick.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-tick.custom {
|
||||
background: transparent none;
|
||||
}
|
||||
.slider-tick.custom::before {
|
||||
line-height: 20px;
|
||||
font-size: 20px;
|
||||
content: '\2605';
|
||||
color: #726204;
|
||||
}
|
||||
.slider-tick.in-selection {
|
||||
background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%);
|
||||
background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%);
|
||||
background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0);
|
||||
opacity: 1;
|
||||
}
|
||||
+1553
-388
File diff suppressed because it is too large
Load Diff
@@ -1,138 +0,0 @@
|
||||
/*!
|
||||
* Slider for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.slider {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
.slider.slider-horizontal {
|
||||
width: 210px;
|
||||
height: 20px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-track {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
margin-top: -5px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-selection {
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-horizontal .slider-handle {
|
||||
margin-left: -10px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
.slider.slider-horizontal .slider-handle.triangle {
|
||||
border-width: 0 10px 10px 10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-bottom-color: #0480be;
|
||||
margin-top: 0;
|
||||
}
|
||||
.slider.slider-vertical {
|
||||
height: 210px;
|
||||
width: 20px;
|
||||
}
|
||||
.slider.slider-vertical .slider-track {
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
margin-left: -5px;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-selection {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.slider.slider-vertical .slider-handle {
|
||||
margin-left: -5px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
.slider.slider-vertical .slider-handle.triangle {
|
||||
border-width: 10px 0 10px 10px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border-left-color: #0480be;
|
||||
margin-left: 0;
|
||||
}
|
||||
.slider input {
|
||||
display: none;
|
||||
}
|
||||
.slider .tooltip-inner {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.slider-track {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
background-color: #f7f7f7;
|
||||
background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-selection {
|
||||
position: absolute;
|
||||
background-color: #f7f7f7;
|
||||
background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5));
|
||||
background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5);
|
||||
background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5);
|
||||
background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.slider-handle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #0e90d2;
|
||||
background-image: -moz-linear-gradient(top, #149bdf, #0480be);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
|
||||
background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
|
||||
background-image: -o-linear-gradient(top, #149bdf, #0480be);
|
||||
background-image: linear-gradient(to bottom, #149bdf, #0480be);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
opacity: 0.8;
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
.slider-handle.round {
|
||||
-webkit-border-radius: 20px;
|
||||
-moz-border-radius: 20px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.slider-handle.triangle {
|
||||
background: transparent none;
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
<div class="umb-editor umb-boolean" ng-controller="Umbraco.PropertyEditors.BooleanController">
|
||||
<input type="checkbox" ng-model="renderModel.value" id="{{model.alias}}" />
|
||||
</div>
|
||||
</div>
|
||||
+4
-3
@@ -44,6 +44,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
|
||||
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
|
||||
$scope.hasDatetimePickerValue = true;
|
||||
$scope.datetimePickerValue = e.date.format($scope.model.config.format);
|
||||
$scope.model.value = $scope.datetimePickerValue;
|
||||
}
|
||||
else {
|
||||
$scope.hasDatetimePickerValue = false;
|
||||
@@ -96,11 +97,11 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
|
||||
});
|
||||
|
||||
if ($scope.hasDatetimePickerValue) {
|
||||
|
||||
//assign value to plugin/picker
|
||||
var dateVal = $scope.model.value ? moment($scope.model.value, $scope.model.config.format) : moment();
|
||||
var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment();
|
||||
|
||||
element.datetimepicker("setValue", dateVal);
|
||||
$scope.datetimePickerValue = moment($scope.model.value).format($scope.model.config.format);
|
||||
$scope.datetimePickerValue = dateVal.format($scope.model.config.format);
|
||||
}
|
||||
|
||||
element.find("input").bind("blur", function() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<ng-form name="datePickerForm">
|
||||
<div class="input-append date datepicker" style="position: relative;" id="datepicker{{model.alias}}">
|
||||
|
||||
<input name="datepicker" data-format="{{model.config.format}}" type="text"
|
||||
<input name="datepicker" data-format="{{model.config.format}}" id="{{model.alias}}" type="text"
|
||||
ng-model="datetimePickerValue"
|
||||
ng-required="model.validation.mandatory"
|
||||
val-server="value"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div>
|
||||
|
||||
<input type="text" name="textbox"
|
||||
<input type="email" name="textbox"
|
||||
ng-model="model.value"
|
||||
id="{{model.alias}}"
|
||||
class="umb-editor umb-textstring textstring"
|
||||
@@ -11,4 +10,4 @@
|
||||
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="required">Required</span>
|
||||
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valEmail">Invalid email</span>
|
||||
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valServer"></span>
|
||||
</div>
|
||||
</div>
|
||||
+22
-25
@@ -144,31 +144,28 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag
|
||||
};
|
||||
angular.module("umbraco")
|
||||
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
|
||||
.run(function(mediaHelper, umbRequestHelper){
|
||||
.run(function(mediaHelper, umbRequestHelper, assetsService){
|
||||
if (mediaHelper && mediaHelper.registerFileResolver) {
|
||||
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
|
||||
if (thumbnail) {
|
||||
|
||||
if (mediaHelper.detectIfImageByExtension(property.value)) {
|
||||
|
||||
var thumbnailUrl = umbRequestHelper.getApiUrl(
|
||||
"imagesApiBaseUrl",
|
||||
"GetBigThumbnail",
|
||||
[{ originalImagePath: property.value }]);
|
||||
|
||||
return thumbnailUrl;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
assetsService.load(["lib/moment/moment-with-locales.js"]).then(
|
||||
function () {
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
|
||||
if (thumbnail) {
|
||||
if (mediaHelper.detectIfImageByExtension(property.value)) {
|
||||
//get default big thumbnail from image processor
|
||||
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500";
|
||||
return thumbnailUrl;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return property.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
return property.value;
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+10
-5
@@ -30,11 +30,16 @@ angular.module("umbraco")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$scope.styles = _.filter( angular.copy($scope.dialogOptions.config.items.styles), function(item){return (item.applyTo === undefined || item.applyTo === $scope.dialogOptions.itemType); });
|
||||
$scope.config = _.filter( angular.copy($scope.dialogOptions.config.items.config), function(item){return (item.applyTo === undefined || item.applyTo === $scope.dialogOptions.itemType); });
|
||||
|
||||
|
||||
// if opening a settings dialog from a control - use only settings from this control
|
||||
// otherwise use editor configuration for row/cell settings
|
||||
if ($scope.dialogOptions.itemType === 'control') {
|
||||
$scope.config = angular.copy($scope.dialogOptions.gridItem.editor.config.settings);
|
||||
$scope.styles = null;
|
||||
} else {
|
||||
$scope.styles = _.filter(angular.copy($scope.dialogOptions.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === $scope.dialogOptions.itemType); });
|
||||
$scope.config = _.filter(angular.copy($scope.dialogOptions.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === $scope.dialogOptions.itemType); });
|
||||
}
|
||||
|
||||
var element = $scope.dialogOptions.gridItem;
|
||||
if(angular.isObject(element.config)){
|
||||
|
||||
@@ -306,7 +306,7 @@ angular.module("umbraco")
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
// *********************************************
|
||||
// Area management functions
|
||||
// *********************************************
|
||||
|
||||
@@ -169,6 +169,15 @@
|
||||
<i class="icon icon-settings" title="@grid_settings" localize="title"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- control setting area -->
|
||||
<div class="cell-tools-edit" ng-style="hasSettings && $first && {'top': '99px'}" ng-if="control.editor.config && control.editor.config.settings">
|
||||
<a class="iconBox"
|
||||
ng-click="editGridItemSettings(control, 'control')"
|
||||
href>
|
||||
<i class="icon icon-hammer"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -261,4 +270,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ angular.module('umbraco')
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
|
||||
if (property.value.src) {
|
||||
if (property.value && property.value.src) {
|
||||
|
||||
if (thumbnail === true) {
|
||||
return property.value.src + "?width=500&mode=max";
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<div class="umb-editor">
|
||||
<input name="integerField" type="number" class="umb-editor umb-number "
|
||||
ng-model="model.value"
|
||||
val-server="value"
|
||||
fix-number min="{{model.config.min}}" max="{{model.config.max}}" step="{{model.config.step}}" />
|
||||
<input name="integerField"
|
||||
type="number"
|
||||
pattern="[0-9]*"
|
||||
class="umb-editor umb-number"
|
||||
ng-model="model.value"
|
||||
id="{{model.alias}}"
|
||||
val-server="value"
|
||||
fix-number min="{{model.config.min}}" max="{{model.config.max}}" step="{{model.config.step}}" />
|
||||
|
||||
<span class="help-inline" val-msg-for="integerField" val-toggle-msg="number">Not a number</span>
|
||||
<span class="help-inline" val-msg-for="integerField" val-toggle-msg="valServer">{{propertyForm.requiredField.errorMsg}}</span>
|
||||
|
||||
+2
-2
@@ -5,14 +5,13 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
|
||||
|
||||
//check the pre-values for multi-picker
|
||||
var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false;
|
||||
var onlyImages = $scope.model.config.onlyImages && $scope.model.config.onlyImages !== '0' ? true : false;
|
||||
|
||||
if (!$scope.model.config.startNodeId) {
|
||||
userService.getCurrentUser().then(function (userData) {
|
||||
$scope.model.config.startNodeId = userData.startMediaId;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setupViewModel() {
|
||||
$scope.images = [];
|
||||
@@ -62,6 +61,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
|
||||
dialogService.mediaPicker({
|
||||
startNodeId: $scope.model.config.startNodeId,
|
||||
multiPicker: multiPicker,
|
||||
onlyImages: onlyImages,
|
||||
callback: function(data) {
|
||||
|
||||
//it's only a single selector, so make it into an array
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<select ng-model="model.value" required name="handle">
|
||||
<option value="round">Round</option>
|
||||
<option value="square">Square</option>
|
||||
<option value="triangle">Triangle</option>
|
||||
</select>
|
||||
|
||||
<span class="help-inline" val-msg-for="handle" val-toggle-msg="required">
|
||||
<localize key="general_required">Required</localize>
|
||||
</span>
|
||||
</div>
|
||||
+4
-2
@@ -3,6 +3,8 @@
|
||||
<option value="horizontal">Horizontal</option>
|
||||
<option value="vertical">Vertical</option>
|
||||
</select>
|
||||
|
||||
<span class="help-inline" val-msg-for="orientation" val-toggle-msg="required">Required</span>
|
||||
|
||||
<span class="help-inline" val-msg-for="orientation" val-toggle-msg="required">
|
||||
<localize key="general_required">Required</localize>
|
||||
</span>
|
||||
</div>
|
||||
@@ -4,6 +4,13 @@
|
||||
if (!$scope.model.config.orientation) {
|
||||
$scope.model.config.orientation = "horizontal";
|
||||
}
|
||||
if (!$scope.model.config.enableRange) {
|
||||
$scope.model.config.enableRange = false;
|
||||
}
|
||||
else {
|
||||
$scope.model.config.enableRange = $scope.model.config.enableRange === "1" ? true : false;
|
||||
}
|
||||
|
||||
if (!$scope.model.config.initVal1) {
|
||||
$scope.model.config.initVal1 = 0;
|
||||
}
|
||||
@@ -34,6 +41,76 @@
|
||||
else {
|
||||
$scope.model.config.step = parseFloat($scope.model.config.step);
|
||||
}
|
||||
|
||||
if (!$scope.model.config.handle) {
|
||||
$scope.model.config.handle = "round";
|
||||
}
|
||||
|
||||
if (!$scope.model.config.reversed) {
|
||||
$scope.model.config.reversed = false;
|
||||
}
|
||||
else {
|
||||
$scope.model.config.reversed = $scope.model.config.reversed === "1" ? true : false;
|
||||
}
|
||||
|
||||
if (!$scope.model.config.tooltip) {
|
||||
$scope.model.config.tooltip = "show";
|
||||
}
|
||||
|
||||
if (!$scope.model.config.tooltipSplit) {
|
||||
$scope.model.config.tooltipSplit = false;
|
||||
}
|
||||
else {
|
||||
$scope.model.config.tooltipSplit = $scope.model.config.tooltipSplit === "1" ? true : false;
|
||||
}
|
||||
|
||||
if ($scope.model.config.tooltipFormat) {
|
||||
$scope.model.config.formatter = function (value) {
|
||||
if (angular.isArray(value) && $scope.model.config.enableRange) {
|
||||
return $scope.model.config.tooltipFormat.replace("{0}", value[0]).replace("{1}", value[1]);
|
||||
} else {
|
||||
return $scope.model.config.tooltipFormat.replace("{0}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scope.model.config.ticks) {
|
||||
$scope.model.config.ticks = [];
|
||||
}
|
||||
else {
|
||||
// returns comma-separated string to an array, e.g. [0, 100, 200, 300, 400]
|
||||
$scope.model.config.ticks = _.map($scope.model.config.ticks.split(','), function (item) {
|
||||
return parseInt(item.trim());
|
||||
});
|
||||
}
|
||||
|
||||
if (!$scope.model.config.ticksPositions) {
|
||||
$scope.model.config.ticksPositions = [];
|
||||
}
|
||||
else {
|
||||
// returns comma-separated string to an array, e.g. [0, 30, 60, 70, 90, 100]
|
||||
$scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) {
|
||||
return parseInt(item.trim());
|
||||
});
|
||||
console.log($scope.model.config.ticksPositions);
|
||||
}
|
||||
|
||||
if (!$scope.model.config.ticksLabels) {
|
||||
$scope.model.config.ticksLabels = [];
|
||||
}
|
||||
else {
|
||||
// returns comma-separated string to an array, e.g. ['$0', '$100', '$200', '$300', '$400']
|
||||
$scope.model.config.ticksLabels = _.map($scope.model.config.ticksLabels.split(','), function (item) {
|
||||
return item.trim();
|
||||
});
|
||||
}
|
||||
|
||||
if (!$scope.model.config.ticksSnapBounds) {
|
||||
$scope.model.config.ticksSnapBounds = 0;
|
||||
}
|
||||
else {
|
||||
$scope.model.config.ticksSnapBounds = parseFloat($scope.model.config.ticksSnapBounds);
|
||||
}
|
||||
|
||||
/** This creates the slider with the model values - it's called on startup and if the model value changes */
|
||||
function createSlider() {
|
||||
@@ -42,9 +119,10 @@
|
||||
var sliderVal = null;
|
||||
|
||||
//configure the model value based on if range is enabled or not
|
||||
if ($scope.model.config.enableRange === "1") {
|
||||
if ($scope.model.config.enableRange == true) {
|
||||
//If no value saved yet - then use default value
|
||||
if (!$scope.model.value) {
|
||||
//If it contains a single value - then also create a new array value
|
||||
if (!$scope.model.value || $scope.model.value.indexOf(",") == -1) {
|
||||
var i1 = parseFloat($scope.model.config.initVal1);
|
||||
var i2 = parseFloat($scope.model.config.initVal2);
|
||||
sliderVal = [
|
||||
@@ -75,18 +153,30 @@
|
||||
}
|
||||
|
||||
//initiate slider, add event handler and get the instance reference (stored in data)
|
||||
var slider = $element.find('.slider-item').slider({
|
||||
var slider = $element.find('.slider-item').bootstrapSlider({
|
||||
max: $scope.model.config.maxVal,
|
||||
min: $scope.model.config.minVal,
|
||||
orientation: $scope.model.config.orientation,
|
||||
selection: "after",
|
||||
selection: $scope.model.config.reversed ? "after" : "before",
|
||||
step: $scope.model.config.step,
|
||||
tooltip: "show",
|
||||
precision: $scope.model.config.precision,
|
||||
tooltip: $scope.model.config.tooltip,
|
||||
tooltip_split: $scope.model.config.tooltipSplit,
|
||||
tooltip_position: $scope.model.config.tooltipPosition,
|
||||
handle: $scope.model.config.handle,
|
||||
reversed: $scope.model.config.reversed,
|
||||
ticks: $scope.model.config.ticks,
|
||||
ticks_positions: $scope.model.config.ticksPositions,
|
||||
ticks_labels: $scope.model.config.ticksLabels,
|
||||
ticks_snap_bounds: $scope.model.config.ticksSnapBounds,
|
||||
formatter: $scope.model.config.formatter,
|
||||
range: $scope.model.config.enableRange,
|
||||
//set the slider val - we cannot do this with data- attributes when using ranges
|
||||
value: sliderVal
|
||||
}).on('slideStop', function () {
|
||||
}).on('slideStop', function (e) {
|
||||
var value = e.value;
|
||||
angularHelper.safeApply($scope, function () {
|
||||
setModelValueFromSlider(slider.getValue());
|
||||
setModelValueFromSlider(value);
|
||||
});
|
||||
}).data('slider');
|
||||
}
|
||||
@@ -95,7 +185,7 @@
|
||||
the model with the currently selected slider value(s) **/
|
||||
function setModelValueFromSlider(sliderVal) {
|
||||
//Get the value from the slider and format it correctly, if it is a range we want a comma delimited value
|
||||
if ($scope.model.config.enableRange === "1") {
|
||||
if ($scope.model.config.enableRange == true) {
|
||||
$scope.model.value = sliderVal.join(",");
|
||||
}
|
||||
else {
|
||||
@@ -122,7 +212,7 @@
|
||||
});
|
||||
|
||||
//load the separate css for the editor to avoid it blocking our js loading
|
||||
assetsService.loadCss("lib/slider/slider.css");
|
||||
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider.css");
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider-custom.css");
|
||||
}
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController);
|
||||
@@ -0,0 +1,11 @@
|
||||
<div>
|
||||
<select ng-model="model.value" required name="tooltip">
|
||||
<option value="show">Show</option>
|
||||
<option value="hide">Hide</option>
|
||||
<option value="always">Always</option>
|
||||
</select>
|
||||
|
||||
<span class="help-inline" val-msg-for="tooltip" val-toggle-msg="required">
|
||||
<localize key="general_required">Required</localize>
|
||||
</span>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user