Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33ccf490ab | |||
| 03ba8c9e14 | |||
| 13b57c7638 | |||
| a8b297903d | |||
| 89b5774106 | |||
| 774f201514 | |||
| 12e2aa679b | |||
| 1978930fa2 | |||
| c4e4f9579b | |||
| 9782f66507 | |||
| e9938afc83 | |||
| fcda9821d0 | |||
| bdc234b18a | |||
| 908afbd264 | |||
| c548ad6a98 | |||
| 99b2a2da38 | |||
| 15ed458ad3 | |||
| 6ce303a918 | |||
| 62d3dc78fa | |||
| 3374f9e023 | |||
| 2675992d82 | |||
| ce29f54663 | |||
| 7261162d8a | |||
| b5f900e789 | |||
| fdab6d5de5 | |||
| ff94eba3a7 | |||
| e200e2fc48 | |||
| 22dde5a421 | |||
| 7f510c107a | |||
| 2ebfd8f3d4 | |||
| fd7930256b | |||
| c24775adb0 | |||
| 217f591db0 | |||
| d453aedd0d | |||
| 4693fb23c8 | |||
| 7848a3095d | |||
| 388eea95e5 | |||
| 6167d1584a | |||
| bfd0c5e309 | |||
| 43669e62b4 | |||
| 6b128ea7d4 | |||
| d5993a4783 | |||
| 86c2a6e8a9 | |||
| 874f464a2b | |||
| b3c41e051f | |||
| a88637910f | |||
| 431e93000e | |||
| 1a821b4073 | |||
| a89b9e9da6 | |||
| 8917701fa3 | |||
| 68b17bff08 | |||
| 7a12060c37 | |||
| 525280d466 | |||
| 496beca8f2 | |||
| 637a22293d | |||
| 3c8517edcb | |||
| 45e6127d00 | |||
| 6eb5d3f7ef | |||
| e6986432df | |||
| 80103e6155 | |||
| 392634eb76 | |||
| 259bc863f0 | |||
| 582bafdefd | |||
| 9924ef2381 | |||
| 859fbaaa05 | |||
| 72991334c4 | |||
| c659d5b85e | |||
| a4bf4b0977 | |||
| bef6ce66f1 | |||
| a18a2198a8 | |||
| 7cf7ac687c | |||
| 7ce0272169 | |||
| acb934e3f1 | |||
| 3aa486a463 | |||
| 5aa0621c90 | |||
| 625b8f7e65 | |||
| e27163f3a7 | |||
| 65ea6f94d3 | |||
| 9158ea1ace | |||
| e8f7f77bb6 | |||
| 75e003185a | |||
| 9d12e1c7df | |||
| b8575ef158 | |||
| d3bb750f98 | |||
| 02eff33d29 | |||
| b43f39a0cb | |||
| d0cb27d1c1 | |||
| 16a1720bf7 | |||
| ea32776174 | |||
| 320b53ae3c | |||
| d987dfb5a2 | |||
| 74806984b3 | |||
| c8c278c7bc | |||
| 13e8e972eb | |||
| a8e4c3eb9a | |||
| d631039c0d | |||
| 113b866485 | |||
| 6c6ef8d144 | |||
| 95b9a46b88 | |||
| 99250cefbb | |||
| dc651a1c42 | |||
| 8fb446960c | |||
| 4e9fd5a223 | |||
| e2c36d7212 | |||
| 62887607ff | |||
| 695cc07912 | |||
| f325794523 | |||
| 3ec6d2b82f | |||
| 46f57a3115 | |||
| d697c590e7 | |||
| a55cd67fd6 | |||
| afa81be102 | |||
| ddd0e8abb5 | |||
| e07f573683 | |||
| 1f88960b3f | |||
| d8edf67b82 | |||
| 365a8e85e9 | |||
| 5f9dddafd8 | |||
| 836297df58 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET release=6.2.0
|
||||
SET release=6.2.2
|
||||
SET comment=
|
||||
SET version=%release%
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms.Core</id>
|
||||
<version>6.2.0</version>
|
||||
<version>6.2.2</version>
|
||||
<title>Umbraco Cms Core Binaries</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
@@ -15,11 +15,13 @@
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[4.0.30506,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.WebHost" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Core" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[4.0.30506, 5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.0, 3.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.20710.0, 3.0.0)" />
|
||||
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.6, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
@@ -29,6 +31,7 @@
|
||||
<dependency id="ClientDependency" version="[1.7.1.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[4.5.11, 6.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -38,7 +41,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Examine.dll" target="lib\Examine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
@@ -69,5 +71,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.XmlSerializers.dll" target="lib\umbraco.XmlSerializers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>UmbracoCms</id>
|
||||
<version>6.1.2</version>
|
||||
<version>6.2.2</version>
|
||||
<title>Umbraco Cms</title>
|
||||
<authors>Umbraco HQ</authors>
|
||||
<owners>Umbraco HQ</owners>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<UmbracoVersion>6.2.0</UmbracoVersion>
|
||||
<UmbracoVersion>6.2.2</UmbracoVersion>
|
||||
</PropertyGroup>
|
||||
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
param($rootPath, $toolsPath, $package, $project)
|
||||
|
||||
if ($project) {
|
||||
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
|
||||
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
|
||||
$copyLogsPath = Join-Path $backupPath "CopyLogs"
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
|
||||
# Create backup folder and logs folder if it doesn't exist yet
|
||||
New-Item -ItemType Directory -Force -Path $backupPath
|
||||
New-Item -ItemType Directory -Force -Path $copyLogsPath
|
||||
|
||||
# After backing up, remove all dlls from bin folder in case dll files are included in the VS project
|
||||
# See: http://issues.umbraco.org/issue/U4-4930
|
||||
$umbracoBinFolder = Join-Path $projectDestinationPath "bin"
|
||||
if(Test-Path $umbracoBinFolder) {
|
||||
$umbracoBinBackupPath = Join-Path $backupPath "bin"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
|
||||
|
||||
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
|
||||
Remove-Item $umbracoBinFolder\*.dll -Force -Confirm:$false
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@ param($rootPath, $toolsPath, $package, $project)
|
||||
if ($project) {
|
||||
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
|
||||
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
|
||||
$copyLogsPath = Join-Path $backupPath "CopyLogs"
|
||||
|
||||
# Create backup folder if it doesn't exist yet
|
||||
# Create backup folder and logs folder if it doesn't exist yet
|
||||
New-Item -ItemType Directory -Force -Path $backupPath
|
||||
New-Item -ItemType Directory -Force -Path $copyLogsPath
|
||||
|
||||
# Create a backup of original web.config
|
||||
$projectDestinationPath = Split-Path $project.FullName -Parent
|
||||
@@ -19,18 +21,26 @@ if ($project) {
|
||||
# Copy umbraco and umbraco_files from package to project folder
|
||||
# This is only done when these folders already exist because we
|
||||
# only want to do this for upgrades
|
||||
$umbracoFolder = Join-Path $projectDestinationPath "Umbraco\"
|
||||
$umbracoFolder = Join-Path $projectDestinationPath "Umbraco"
|
||||
if(Test-Path $umbracoFolder) {
|
||||
$umbracoFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco"
|
||||
Copy-Item $umbracoFolder $backupPath -Force
|
||||
robocopy $umbracoFolderSource $umbracoFolder /e /xf UI.xml
|
||||
|
||||
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
|
||||
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
|
||||
|
||||
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
|
||||
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
|
||||
}
|
||||
|
||||
$umbracoClientFolder = Join-Path $projectDestinationPath "Umbraco_Client"
|
||||
if(Test-Path $umbracoClientFolder) {
|
||||
$umbracoClientFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco_Client"
|
||||
Copy-Item $umbracoClientFolder $backupPath -Force
|
||||
robocopy $umbracoFolderSource $umbracoClientFolder /e
|
||||
|
||||
$umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
|
||||
New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
|
||||
|
||||
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
|
||||
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
|
||||
}
|
||||
# Open readme.txt file
|
||||
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Sql\Total.sql" />
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Sql\Version4_1_Upgrade.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -1,218 +1,204 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
{
|
||||
protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
protected abstract DictionaryCacheWrapper DictionaryCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears everything in umbraco's runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not clear other stuff the user has put in httpruntime.cache!
|
||||
/// </remarks>
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null)
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
if (DictionaryCache[GetCacheKey(key)] == null) return;
|
||||
DictionaryCache.Remove(GetCacheKey(key)); ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type name as the
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT;
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT && predicate(k, (T)v);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var keysToRemove = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
keysToRemove.Add(c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from object item in DictionaryCache
|
||||
select new DictionaryItemWrapper(item)
|
||||
into c
|
||||
where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
var found = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
found.Add(c.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = DictionaryCache.Get(GetCacheKey(cacheKey));
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
/// <summary>
|
||||
/// We prefix all cache keys with this so that we know which ones this class has created when
|
||||
/// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create.
|
||||
/// </summary>
|
||||
// prefix cache keys so we know which one are ours
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
// manupulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
// read-write lock the underlying cache
|
||||
protected abstract IDisposable ReadLock { get; }
|
||||
protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
using (WriteLock)
|
||||
{
|
||||
RemoveEntry(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
// run predicate on the 'public key' part only, ie without prefix
|
||||
&& predicate(((string)x.Key).Substring(plen), (T)value);
|
||||
}))
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
Lazy<object> result;
|
||||
using (ReadLock)
|
||||
{
|
||||
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryCacheWrapper : IEnumerable
|
||||
{
|
||||
private readonly IEnumerable _inner;
|
||||
private readonly Func<object, object> _get;
|
||||
private readonly Action<object> _remove;
|
||||
|
||||
public DictionaryCacheWrapper(
|
||||
IEnumerable inner,
|
||||
Func<object, object> get,
|
||||
Action<object> remove)
|
||||
{
|
||||
_inner = inner;
|
||||
_get = get;
|
||||
_remove = remove;
|
||||
}
|
||||
|
||||
public object this[object key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public object Get(object key)
|
||||
{
|
||||
return _get(key);
|
||||
}
|
||||
|
||||
public void Remove(object key)
|
||||
{
|
||||
_remove(key);
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return _inner.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryItemWrapper
|
||||
{
|
||||
public DictionaryItemWrapper(dynamic item)
|
||||
{
|
||||
Key = item.Key;
|
||||
Value = item.Value;
|
||||
}
|
||||
|
||||
public object Key { get; private set; }
|
||||
public object Value { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -12,36 +11,115 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
{
|
||||
private readonly Func<HttpContextBase> _context;
|
||||
// context provider
|
||||
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
||||
// that is initialized with a method that returns the "current" context.
|
||||
// NOTE
|
||||
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
||||
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
||||
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
||||
//private readonly Func<HttpContextBase> _context;
|
||||
|
||||
public HttpRequestCacheProvider(HttpContext context)
|
||||
// NOTE
|
||||
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
||||
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
||||
// _context1 is null?
|
||||
//private readonly HttpContextBase _context1;
|
||||
//private readonly Func<HttpContext> _context2;
|
||||
private readonly HttpContextBase _context;
|
||||
|
||||
private IDictionary ContextItems
|
||||
{
|
||||
_context = () => new HttpContextWrapper(context);
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
}
|
||||
|
||||
public HttpRequestCacheProvider(Func<HttpContextBase> context)
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
{
|
||||
//_context2 = context;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return ContextItems.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
ContextItems.Remove(key);
|
||||
}
|
||||
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
return ContextItems[key];
|
||||
}
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
// there's no difference between ReadLock and WriteLock here
|
||||
get { return WriteLock; }
|
||||
}
|
||||
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
// NOTE
|
||||
// could think about just overriding base.Locker to return a different
|
||||
// object but then we'd create a ReaderWriterLockSlim per request,
|
||||
// which is less efficient than just using a basic monitor lock.
|
||||
|
||||
get
|
||||
{
|
||||
var ctx = _context();
|
||||
return new DictionaryCacheWrapper(
|
||||
ctx.Items,
|
||||
o => ctx.Items[o],
|
||||
o => ctx.Items.Remove(o));
|
||||
return new MonitorLock(ContextItems.SyncRoot);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var ctx = _context();
|
||||
var ck = GetCacheKey(cacheKey);
|
||||
return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem());
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (WriteLock)
|
||||
{
|
||||
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
ContextItems[cacheKey] = result;
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Logging;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -14,47 +13,51 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
{
|
||||
// locker object that supports upgradeable read locking
|
||||
// does not need to support recursion if we implement the cache correctly and ensure
|
||||
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
private readonly DictionaryCacheWrapper _wrapper;
|
||||
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
_wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString()));
|
||||
}
|
||||
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
get { return _wrapper; }
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return _cache.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate
|
||||
/// </summary>
|
||||
public override void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
var key = c.Key.ToString();
|
||||
if (_cache[key] != null
|
||||
&& _cache[key] is T
|
||||
&& predicate(key, (T)_cache[key]))
|
||||
{
|
||||
_cache.Remove(c.Key.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<CacheHelper>("Cache clearing error", e);
|
||||
}
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
protected override object GetEntry(string key)
|
||||
{
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override IDisposable ReadLock
|
||||
{
|
||||
get { return new ReadLock(_locker); }
|
||||
}
|
||||
|
||||
protected override IDisposable WriteLock
|
||||
{
|
||||
get { return new WriteLock(_locker); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
@@ -81,25 +84,64 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
|
||||
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
|
||||
// nasty performance issues.
|
||||
|
||||
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
|
||||
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
|
||||
// application lock has been released.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may produce a null value.
|
||||
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
|
||||
// So if we find an entry in the cache that already has its value created and is null,
|
||||
// pretend it was not there. If value is not already created, wait... and return null, that's
|
||||
// what prior code did.
|
||||
|
||||
// NOTE
|
||||
// The Lazy value creation may throw.
|
||||
|
||||
// So... the null value _will_ be in the cache but never returned
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
// Fast!
|
||||
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
|
||||
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
|
||||
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
|
||||
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
|
||||
// it's worth it, though...
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
var result = DictionaryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
var value = result == null ? null : GetSafeLazyValue(result);
|
||||
if (value != null) return value;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw if getCacheItem throws, but this is the only place where
|
||||
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
|
||||
// and pretend exceptions were never inserted into cache to begin with.
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
@@ -112,6 +154,10 @@ namespace Umbraco.Core.Cache
|
||||
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
@@ -124,15 +170,22 @@ namespace Umbraco.Core.Cache
|
||||
/// <param name="dependency"></param>
|
||||
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
var result = getCacheItem();
|
||||
if (result == null) return;
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
@@ -144,5 +197,7 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,37 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
// an object that represent a value that has not been created yet
|
||||
protected readonly object ValueNotCreated = new object();
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
{
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
|
||||
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
return lazy.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Clear
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
MemoryCache.DisposeIfDisposable();
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
@@ -35,7 +55,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
if (MemoryCache[key] == null) return;
|
||||
MemoryCache.Remove(key);
|
||||
@@ -44,131 +64,179 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof (T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT)
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
return value == null || value.GetType() == typeOfT;
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
{
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
return value.GetType() == typeOfT
|
||||
&& predicate(x.Key, (T) value);
|
||||
})
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
KeyValuePair<string, object>[] entries;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
entries = MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
return result;
|
||||
Lazy<object> result;
|
||||
using (new ReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal,CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>;
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
object result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = new Lazy<object>(getCacheItem);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
new HttpRequestCacheProvider())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
new HttpRequestCacheProvider())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("6.2.0");
|
||||
private static readonly Version Version = new Version("6.2.2");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace Umbraco.Core.Dynamics
|
||||
public object Value { get { return _source == null ? _value : _source.Value; } }
|
||||
public object XPathValue { get { return Value == null ? null : Value.ToString(); } }
|
||||
|
||||
public string Alias { get { return PropertyTypeAlias; }}
|
||||
public Guid Version { get { return Guid.Empty; }}
|
||||
|
||||
// implements IHtmlString.ToHtmlString
|
||||
public string ToHtmlString()
|
||||
{
|
||||
|
||||
@@ -15,43 +15,13 @@ namespace Umbraco.Core
|
||||
public static IEnumerable<IEnumerable<T>> InGroupsOf<T>(this IEnumerable<T> source, int groupSize)
|
||||
{
|
||||
if (source == null)
|
||||
throw new NullReferenceException("source");
|
||||
throw new ArgumentNullException("source");
|
||||
if (groupSize <= 0)
|
||||
throw new ArgumentException("Must be greater than zero.", "groupSize");
|
||||
|
||||
// enumerate the source only once!
|
||||
return new InGroupsEnumerator<T>(source, groupSize).Groups();
|
||||
}
|
||||
|
||||
// this class makes sure that the source is enumerated only ONCE
|
||||
// which means that when it is enumerated, the actual groups content
|
||||
// has to be evaluated at the same time, and stored in an array.
|
||||
private class InGroupsEnumerator<T>
|
||||
{
|
||||
private readonly IEnumerator<T> _source;
|
||||
private readonly int _count;
|
||||
private bool _mightHaveNext;
|
||||
|
||||
public InGroupsEnumerator(IEnumerable<T> source, int count)
|
||||
{
|
||||
_source = source.GetEnumerator();
|
||||
_count = count;
|
||||
_mightHaveNext = true;
|
||||
}
|
||||
|
||||
public IEnumerable<IEnumerable<T>> Groups()
|
||||
{
|
||||
while (_mightHaveNext && _source.MoveNext())
|
||||
yield return Group().ToArray(); // see note above
|
||||
}
|
||||
|
||||
private IEnumerable<T> Group()
|
||||
{
|
||||
var c = 0;
|
||||
do
|
||||
{
|
||||
yield return _source.Current;
|
||||
} while (++c < _count && _source.MoveNext());
|
||||
_mightHaveNext = c == _count;
|
||||
}
|
||||
return source
|
||||
.Select((x, i) => Tuple.Create(i / groupSize, x))
|
||||
.GroupBy(t => t.Item1, t => t.Item2);
|
||||
}
|
||||
|
||||
/// <summary>The distinct by.</summary>
|
||||
|
||||
@@ -19,7 +19,10 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static void CopyFile(this IFileSystem fs, string path, string newPath)
|
||||
{
|
||||
fs.AddFile(newPath, fs.OpenFile(path));
|
||||
using (var stream = fs.OpenFile(path))
|
||||
{
|
||||
fs.AddFile(newPath, stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetExtension(this IFileSystem fs, string path)
|
||||
|
||||
@@ -93,8 +93,9 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideIfExists)
|
||||
{
|
||||
if (FileExists(path) && !overrideIfExists) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
var exists = FileExists(path);
|
||||
if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
EnsureDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
if (stream.CanSeek)
|
||||
|
||||
@@ -133,16 +133,23 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (_size == null)
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
if (_fs.FileExists(Path))
|
||||
{
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
var fs = _fs.OpenFile(Path);
|
||||
var image = Image.FromStream(fs);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
fs.Close();
|
||||
image.Dispose();
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = new Size(-1, -1);
|
||||
}
|
||||
}
|
||||
return _size.Value;
|
||||
}
|
||||
|
||||
@@ -222,6 +222,28 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes characters that are not valide XML characters from all entity properties
|
||||
/// of type string. See: http://stackoverflow.com/a/961504/5018
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it.
|
||||
/// </remarks>
|
||||
/// <param name="entity"></param>
|
||||
public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity)
|
||||
{
|
||||
entity.Name = entity.Name.ToValidXmlString();
|
||||
foreach (var property in entity.Properties)
|
||||
{
|
||||
if (property.Value is string)
|
||||
{
|
||||
var value = (string)property.Value;
|
||||
property.Value = value.ToValidXmlString();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the IContentBase has children
|
||||
/// </summary>
|
||||
|
||||
@@ -133,29 +133,6 @@ namespace Umbraco.Core.Models
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentType)base.DeepClone();
|
||||
var propertyGroups = PropertyGroups.Select(x => (PropertyGroup)x.DeepClone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
//set the property types that are not part of a group
|
||||
clone.PropertyTypes = PropertyTypeCollection
|
||||
.Where(x => x.PropertyGroupId == null)
|
||||
.Select(x => (PropertyType)x.DeepClone()).ToList();
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
|
||||
@@ -351,7 +351,9 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this ContentType
|
||||
/// </summary>
|
||||
/// <remarks>A PropertyGroup corresponds to a Tab in the UI</remarks>
|
||||
/// <remarks>
|
||||
/// A PropertyGroup corresponds to a Tab in the UI
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual PropertyGroupCollection PropertyGroups
|
||||
{
|
||||
@@ -367,7 +369,13 @@ namespace Umbraco.Core.Models
|
||||
/// List of PropertyTypes available on this ContentType.
|
||||
/// This list aggregates PropertyTypes across the PropertyGroups.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
|
||||
/// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
|
||||
/// data. We manually clone this instead.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DoNotClone]
|
||||
public virtual IEnumerable<PropertyType> PropertyTypes
|
||||
{
|
||||
get
|
||||
@@ -383,6 +391,14 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property type collection containing types that are non-groups - used for tests
|
||||
/// </summary>
|
||||
internal IEnumerable<PropertyType> NonGroupedPropertyTypes
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A boolean flag indicating if a property type has been removed from this instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -579,5 +595,19 @@ namespace Umbraco.Core.Models
|
||||
propertyType.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeBase)base.DeepClone();
|
||||
|
||||
//need to manually wire up the event handlers for the property type collections - we've ensured
|
||||
// its ignored from the auto-clone process because its return values are unions, not raw and
|
||||
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
|
||||
|
||||
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
|
||||
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to attribute properties that have a setter and are a reference type
|
||||
/// that should be ignored for cloning when using the DeepCloneHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// This attribute must be used:
|
||||
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
|
||||
///
|
||||
/// This attribute should not be used:
|
||||
/// * when the property is virtual
|
||||
/// * when the setter performs additional required logic other than just setting the underlying field
|
||||
///
|
||||
/// </remarks>
|
||||
internal class DoNotCloneAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class DeepCloneHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to avoid constant reflection (perf)
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
|
||||
/// </summary>
|
||||
@@ -23,14 +49,18 @@ namespace Umbraco.Core.Models
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
var refProperties = inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//reference type but not string
|
||||
x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false);
|
||||
var refProperties = PropCache.GetOrAdd(inputType, type =>
|
||||
inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
Attribute.GetCustomAttribute(x, typeof(DoNotCloneAttribute)) == null
|
||||
//reference type but not string
|
||||
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false)
|
||||
.ToArray());
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -23,5 +20,5 @@ namespace Umbraco.Core.Models
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
@@ -56,5 +58,8 @@ namespace Umbraco.Core.Models
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object XPathValue { get; }
|
||||
|
||||
string Alias { get; }
|
||||
Guid Version { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Type for a Backoffice User
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserType : Entity, IUserType
|
||||
{
|
||||
[DataMember]
|
||||
public string Alias { get; set; }
|
||||
private string _alias;
|
||||
private string _name;
|
||||
private IEnumerable<string> _permissions;
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Name);
|
||||
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<UserType, string>(x => x.Alias);
|
||||
private static readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo<UserType, IEnumerable<string>>(x => x.Permissions);
|
||||
|
||||
[DataMember]
|
||||
public string Name { get; set; }
|
||||
public string Alias
|
||||
{
|
||||
get { return _alias; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The set of default permissions for the user type
|
||||
@@ -30,6 +58,17 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public IEnumerable<string> Permissions { get; set; }
|
||||
public IEnumerable<string> Permissions
|
||||
{
|
||||
get { return _permissions; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_permissions = value;
|
||||
return _permissions;
|
||||
}, _permissions, PermissionsSelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,15 @@
|
||||
/// <summary>
|
||||
/// Represents a stored pre-value field value
|
||||
/// </summary>
|
||||
public class PreValue
|
||||
public class PreValue : IDeepCloneable
|
||||
{
|
||||
public PreValue(int id, string value, int sortOrder)
|
||||
{
|
||||
Id = id;
|
||||
Value = value;
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public PreValue(int id, string value)
|
||||
{
|
||||
Value = value;
|
||||
@@ -25,5 +32,17 @@
|
||||
/// The database id for the pre-value field value
|
||||
/// </summary>
|
||||
public int Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sort order stored for the pre-value field value
|
||||
/// </summary>
|
||||
public int SortOrder { get; private set; }
|
||||
|
||||
public virtual object DeepClone()
|
||||
{
|
||||
//Memberwise clone on PreValue will work since it doesn't have any deep elements
|
||||
var clone = (PreValue)MemberwiseClone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
|
||||
/// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format.
|
||||
/// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key.
|
||||
/// </remarks>
|
||||
public class PreValueCollection
|
||||
public class PreValueCollection : IDeepCloneable
|
||||
{
|
||||
private IDictionary<string, PreValue> _preValuesAsDictionary;
|
||||
private IEnumerable<PreValue> _preValuesAsArray;
|
||||
@@ -82,5 +82,21 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (PreValueCollection) MemberwiseClone();
|
||||
if (_preValuesAsArray != null)
|
||||
{
|
||||
clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue)x.DeepClone()).ToArray();
|
||||
}
|
||||
if (_preValuesAsDictionary != null)
|
||||
{
|
||||
clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue)x.Value.DeepClone());
|
||||
}
|
||||
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
public abstract object DataValue { get; }
|
||||
public abstract object Value { get; }
|
||||
public abstract object XPathValue { get; }
|
||||
|
||||
public string Alias { get { return PropertyTypeAlias; } }
|
||||
public Guid Version { get { return Guid.Empty; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an equivalent to the c# lock statement, to be used in a using block.
|
||||
/// </summary>
|
||||
/// <remarks>Ie replace <c>lock (o) {...}</c> by <c>using (new MonitorLock(o)) { ... }</c></remarks>
|
||||
public class MonitorLock : IDisposable
|
||||
{
|
||||
private readonly object _locker;
|
||||
private readonly bool _entered;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MonitorLock"/> class with an object to lock.
|
||||
/// </summary>
|
||||
/// <param name="locker">The object to lock.</param>
|
||||
/// <remarks>Should always be used within a using block.</remarks>
|
||||
public MonitorLock(object locker)
|
||||
{
|
||||
_locker = locker;
|
||||
_entered = false;
|
||||
System.Threading.Monitor.Enter(_locker, ref _entered);
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
if (_entered)
|
||||
System.Threading.Monitor.Exit(_locker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,11 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
|
||||
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
|
||||
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
|
||||
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
|
||||
/// </summary>
|
||||
|
||||
@@ -128,9 +128,9 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
public void Save(Type type, IEntity entity)
|
||||
{
|
||||
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
|
||||
entity = (IEntity)entity.DeepClone();
|
||||
var clone = (IEntity)entity.DeepClone();
|
||||
|
||||
var key = GetCompositeId(type, entity.Id);
|
||||
var key = GetCompositeId(type, clone.Id);
|
||||
|
||||
_keyTracker.TryAdd(key);
|
||||
|
||||
@@ -139,11 +139,11 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
if (_memoryCache != null)
|
||||
{
|
||||
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
_memoryCache.Set(key, clone, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
|
||||
HttpRuntime.Cache.Insert(key, clone, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
ComputerName = entity.ComputerName
|
||||
};
|
||||
if (entity.HasIdentity)
|
||||
dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
+14
-3
@@ -1,5 +1,7 @@
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero
|
||||
{
|
||||
@@ -18,9 +20,18 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero
|
||||
}
|
||||
else
|
||||
{
|
||||
//These are the old aliases
|
||||
Delete.ForeignKey("FK_umbracoUser2app_umbracoApp").OnTable("umbracoUser2app");
|
||||
Delete.ForeignKey("FK_umbracoUser2app_umbracoUser").OnTable("umbracoUser2app");
|
||||
//These are the old aliases, before removing them, check they exist
|
||||
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
|
||||
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser2app") && x.Item3.InvariantEquals("FK_umbracoUser2app_umbracoApp")))
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser2app_umbracoApp").OnTable("umbracoUser2app");
|
||||
}
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser2app") && x.Item3.InvariantEquals("FK_umbracoUser2app_umbracoUser")))
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser2app_umbracoUser").OnTable("umbracoUser2app");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ namespace Umbraco.Core.Persistence
|
||||
if (overwrite && tableExist)
|
||||
{
|
||||
db.DropTable(tableName);
|
||||
tableExist = false;
|
||||
}
|
||||
|
||||
if (tableExist == false)
|
||||
|
||||
@@ -245,6 +245,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
var factory = new ContentFactory(NodeObjectTypeId, entity.Id);
|
||||
var dto = factory.BuildDto(entity);
|
||||
@@ -350,6 +353,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id);
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
|
||||
{
|
||||
|
||||
@@ -295,8 +295,8 @@ AND umbracoNode.id <> @id",
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
return cached.First();
|
||||
//return from the cache, ensure it's a cloned result
|
||||
return (PreValueCollection)cached.First().DeepClone();
|
||||
}
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
|
||||
@@ -144,6 +144,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
((DictionaryItem)entity).AddingEntity();
|
||||
|
||||
foreach (var translation in entity.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
var factory = new DictionaryItemFactory();
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
@@ -165,6 +168,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
((Entity)entity).UpdatingEntity();
|
||||
|
||||
foreach (var translation in entity.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
var factory = new DictionaryItemFactory();
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
|
||||
@@ -12,6 +12,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public interface IRepositoryVersionable<TId, TEntity> : IRepositoryQueryable<TId, TEntity>
|
||||
where TEntity : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the total count of entities
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias"></param>
|
||||
/// <returns></returns>
|
||||
int Count(string contentTypeAlias = null);
|
||||
|
||||
int CountChildren(int parentId, string contentTypeAlias = null);
|
||||
|
||||
int CountDescendants(int parentId, string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var languageDto = Database.First<LanguageDto>(sql);
|
||||
var languageDto = Database.FirstOrDefault<LanguageDto>(sql);
|
||||
if (languageDto == null)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -213,6 +213,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
@@ -280,6 +283,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Ensure unique name on the same level
|
||||
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id);
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("ParentId"))
|
||||
{
|
||||
@@ -413,4 +419,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return currentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
((Member)entity).AddingEntity();
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
var factory = new MemberFactory(NodeObjectTypeId, entity.Id);
|
||||
var dto = factory.BuildDto(entity);
|
||||
|
||||
@@ -277,6 +280,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//Updates Modified date
|
||||
((Member)entity).UpdatingEntity();
|
||||
|
||||
//Ensure that strings don't contain characters that are invalid in XML
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
var dirtyEntity = (ICanBeDirty)entity;
|
||||
|
||||
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
|
||||
|
||||
@@ -130,6 +130,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public IEnumerable<TEntity> GetAll(params TId[] ids)
|
||||
{
|
||||
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
|
||||
ids = ids.Distinct().ToArray();
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
var entities = _cache.GetByIds(
|
||||
|
||||
@@ -64,6 +64,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public override IEnumerable<Script> GetAll(params string[] ids)
|
||||
{
|
||||
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
|
||||
ids = ids.Distinct().ToArray();
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
foreach (var id in ids)
|
||||
|
||||
@@ -85,6 +85,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public override IEnumerable<Stylesheet> GetAll(params string[] ids)
|
||||
{
|
||||
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
|
||||
ids = ids.Distinct().ToArray();
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
foreach (var id in ids)
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
|
||||
"UPDATE cmsDocument SET templateId = NULL WHERE nodeId = @Id",
|
||||
"UPDATE cmsDocument SET templateId = NULL WHERE templateId = @Id",
|
||||
"DELETE FROM cmsDocumentType WHERE templateNodeId = @Id",
|
||||
"DELETE FROM cmsTemplate WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
|
||||
@@ -93,6 +93,90 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#endregion
|
||||
|
||||
public int CountDescendants(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var pathMatch = parentId == -1
|
||||
? "-1,"
|
||||
: "," + parentId + ",";
|
||||
var sql = new Sql();
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<NodeDto>(x => x.Path.Contains(pathMatch));
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<NodeDto>(x => x.Path.Contains(pathMatch))
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
}
|
||||
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
|
||||
public int CountChildren(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var sql = new Sql();
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<NodeDto>(x => x.ParentId == parentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<NodeDto>(x => x.ParentId == parentId)
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
}
|
||||
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the total count of entities
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias"></param>
|
||||
/// <returns></returns>
|
||||
public int Count(string contentTypeAlias = null)
|
||||
{
|
||||
var sql = new Sql();
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
}
|
||||
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a fix for U4-1407 - when property types are added to a content type - the property of the entity are not actually created
|
||||
/// and we get YSODs
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Publishing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to perform scheduled publishing/unpublishing
|
||||
/// </summary>
|
||||
internal class ScheduledPublisher
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public ScheduledPublisher(IContentService contentService)
|
||||
{
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
public void CheckPendingAndProcess()
|
||||
{
|
||||
foreach (var d in _contentService.GetContentForRelease())
|
||||
{
|
||||
try
|
||||
{
|
||||
d.ReleaseDate = null;
|
||||
var result = _contentService.SaveAndPublishWithStatus(d, (int)d.GetWriterProfile().Id);
|
||||
if (result.Success == false)
|
||||
{
|
||||
if (result.Exception != null)
|
||||
{
|
||||
LogHelper.Error<ScheduledPublisher>("Could not published the document (" + d.Id + ") based on it's scheduled release, status result: " + result.Result.StatusType, result.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<ScheduledPublisher>("Could not published the document (" + d.Id + ") based on it's scheduled release. Status result: " + result.Result.StatusType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogHelper.Error<ScheduledPublisher>(string.Format("Error publishing node {0}", d.Id), ee);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
foreach (var d in _contentService.GetContentForExpiration())
|
||||
{
|
||||
try
|
||||
{
|
||||
d.ExpireDate = null;
|
||||
_contentService.UnPublish(d, (int)d.GetWriterProfile().Id);
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogHelper.Error<ScheduledPublisher>(string.Format("Error unpublishing node {0}", d.Id), ee);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ namespace Umbraco.Core.Security
|
||||
_enablePasswordReset = config.GetValue("enablePasswordReset", false);
|
||||
_requiresQuestionAndAnswer = config.GetValue("requiresQuestionAndAnswer", false);
|
||||
_requiresUniqueEmail = config.GetValue("requiresUniqueEmail", true);
|
||||
_maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
|
||||
_maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 20, false, 0);
|
||||
_passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
|
||||
_minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80);
|
||||
_minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", DefaultMinNonAlphanumericChars, true, 0x80);
|
||||
|
||||
@@ -62,7 +62,12 @@ namespace Umbraco.Core.Security
|
||||
return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider];
|
||||
}
|
||||
|
||||
public static MembershipUser GetCurrentUser(this MembershipProvider membershipProvider)
|
||||
/// <summary>
|
||||
/// Returns the currently logged in MembershipUser and flags them as being online - use sparingly (i.e. login)
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static MembershipUser GetCurrentUserOnline(this MembershipProvider membershipProvider)
|
||||
{
|
||||
var username = membershipProvider.GetCurrentUserName();
|
||||
return username.IsNullOrWhiteSpace()
|
||||
@@ -70,6 +75,19 @@ namespace Umbraco.Core.Security
|
||||
: membershipProvider.GetUser(username, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently logged in MembershipUser
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static MembershipUser GetCurrentUser(this MembershipProvider membershipProvider)
|
||||
{
|
||||
var username = membershipProvider.GetCurrentUserName();
|
||||
return username.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: membershipProvider.GetUser(username, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just returns the current user's login name (just a wrapper).
|
||||
/// </summary>
|
||||
|
||||
@@ -74,6 +74,33 @@ namespace Umbraco.Core.Services
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
public int Count(string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
return repository.Count(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public int CountChildren(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
return repository.CountChildren(parentId, contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public int CountDescendants(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
return repository.CountDescendants(parentId, contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a single permission to the current content item for the specified user ids
|
||||
/// </summary>
|
||||
@@ -1263,8 +1290,8 @@ namespace Umbraco.Core.Services
|
||||
if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs<IContent>(content), this))
|
||||
return false;
|
||||
|
||||
//TODO: Do some stuff here.. ... what is it supposed to do ?
|
||||
// pretty sure all that legacy stuff did was raise an event? and we no longer have IActionHandlers so no worries there.
|
||||
//Save before raising event
|
||||
Save(content, userId);
|
||||
|
||||
SentToPublish.RaiseEvent(new SendToPublishEventArgs<IContent>(content, false), this);
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IContentService : IService
|
||||
{
|
||||
int Count(string contentTypeAlias = null);
|
||||
int CountChildren(int parentId, string contentTypeAlias = null);
|
||||
int CountDescendants(int parentId, string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a single permission to the current content item for the specified user ids
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,10 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IMediaService : IService
|
||||
{
|
||||
int Count(string contentTypeAlias = null);
|
||||
int CountChildren(int parentId, string contentTypeAlias = null);
|
||||
int CountDescendants(int parentId, string contentTypeAlias = null);
|
||||
|
||||
IEnumerable<IMedia> GetByIds(IEnumerable<int> ids);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IMemberService : IMembershipMemberService
|
||||
{
|
||||
int Count(string contentTypeAlias = null);
|
||||
|
||||
IMember CreateMember(string username, string email, string name, string memberTypeAlias);
|
||||
IMember CreateMember(string username, string email, string name, IMemberType memberType);
|
||||
IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias);
|
||||
|
||||
@@ -237,6 +237,15 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>Returns <c>True</c> if any relations exist between the entities, otherwise <c>False</c></returns>
|
||||
bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the Parent relation</param>
|
||||
/// <param name="childId">Id of the Child relation</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exist between the entities, otherwise <c>False</c></returns>
|
||||
bool AreRelated(int parentId, int childId, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a <see cref="Relation"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -251,6 +251,33 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public int Count(string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
return repository.Count(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public int CountChildren(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
return repository.CountChildren(parentId, contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public int CountDescendants(int parentId, string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
return repository.CountDescendants(parentId, contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IMedia"/> object by Id
|
||||
/// </summary>
|
||||
|
||||
@@ -642,6 +642,15 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public int Count(string contentTypeAlias = null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
|
||||
{
|
||||
return repository.Count(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a member object
|
||||
/// </summary>
|
||||
|
||||
@@ -773,6 +773,7 @@ namespace Umbraco.Core.Services
|
||||
var items = new List<IDictionaryItem>();
|
||||
foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem"))
|
||||
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, parentId));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -788,6 +789,7 @@ namespace Umbraco.Core.Services
|
||||
dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId);
|
||||
_localizationService.Save(dictionaryItem);
|
||||
items.Add(dictionaryItem);
|
||||
|
||||
items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, dictionaryItem.Key));
|
||||
return items;
|
||||
}
|
||||
@@ -817,7 +819,8 @@ namespace Umbraco.Core.Services
|
||||
private static bool DictionaryValueIsNew(IEnumerable<IDictionaryTranslation> translations, XElement valueElement)
|
||||
{
|
||||
return translations.All(t =>
|
||||
String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, StringComparison.InvariantCultureIgnoreCase) != 0
|
||||
String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value,
|
||||
StringComparison.InvariantCultureIgnoreCase) != 0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,10 +205,11 @@ namespace Umbraco.Core.Services
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
var provider = MembershipProviderExtensions.GetUsersMembershipProvider();
|
||||
if (provider.IsUmbracoMembershipProvider())
|
||||
{
|
||||
provider.ChangePassword(user.Username, "", password);
|
||||
}
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider() == false)
|
||||
throw new NotSupportedException("When using a non-Umbraco membership provider you must change the user password by using the MembershipProvider.ChangePassword method");
|
||||
|
||||
provider.ChangePassword(user.Username, "", password);
|
||||
|
||||
//go re-fetch the member and update the properties that may have changed
|
||||
var result = GetByUsername(user.Username);
|
||||
@@ -219,8 +220,6 @@ namespace Umbraco.Core.Services
|
||||
user.LastPasswordChangeDate = result.LastPasswordChangeDate;
|
||||
user.UpdateDate = user.UpdateDate;
|
||||
}
|
||||
|
||||
throw new NotSupportedException("When using a non-Umbraco membership provider you must change the user password by using the MembershipProvider.ChangePassword method");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,13 +30,14 @@ namespace Umbraco.Core
|
||||
|
||||
private static readonly char[] ToCSharpHexDigitLower = "0123456789abcdef".ToCharArray();
|
||||
private static readonly char[] ToCSharpEscapeChars;
|
||||
|
||||
|
||||
static StringExtensions()
|
||||
{
|
||||
var escapes = new[] { "\aa", "\bb", "\ff", "\nn", "\rr", "\tt", "\vv", "\"\"", "\\\\", "??", "\00" };
|
||||
ToCSharpEscapeChars = new char[escapes.Max(e => e[0]) + 1];
|
||||
foreach (var escape in escapes)
|
||||
ToCSharpEscapeChars[escape[0]] = escape[1];
|
||||
|
||||
}
|
||||
|
||||
internal static string ReplaceNonAlphanumericChars(this string input, char replacement)
|
||||
@@ -73,69 +74,70 @@ namespace Umbraco.Core
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt the string using the MachineKey in medium trust
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to be encrypted.</param>
|
||||
/// <returns>The encrypted string.</returns>
|
||||
public static string EncryptWithMachineKey(this string value)
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt the string using the MachineKey in medium trust
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to be encrypted.</param>
|
||||
/// <returns>The encrypted string.</returns>
|
||||
public static string EncryptWithMachineKey(this string value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
string valueToEncrypt = value;
|
||||
List<string> parts = new List<string>();
|
||||
string valueToEncrypt = value;
|
||||
List<string> parts = new List<string>();
|
||||
|
||||
const int EncrpytBlockSize = 500;
|
||||
const int EncrpytBlockSize = 500;
|
||||
|
||||
while (valueToEncrypt.Length > EncrpytBlockSize)
|
||||
{
|
||||
parts.Add(valueToEncrypt.Substring(0, EncrpytBlockSize));
|
||||
valueToEncrypt = valueToEncrypt.Remove(0, EncrpytBlockSize);
|
||||
}
|
||||
while (valueToEncrypt.Length > EncrpytBlockSize)
|
||||
{
|
||||
parts.Add(valueToEncrypt.Substring(0, EncrpytBlockSize));
|
||||
valueToEncrypt = valueToEncrypt.Remove(0, EncrpytBlockSize);
|
||||
}
|
||||
|
||||
if (valueToEncrypt.Length > 0)
|
||||
{
|
||||
parts.Add(valueToEncrypt);
|
||||
}
|
||||
if (valueToEncrypt.Length > 0)
|
||||
{
|
||||
parts.Add(valueToEncrypt);
|
||||
}
|
||||
|
||||
StringBuilder encrpytedValue = new StringBuilder();
|
||||
StringBuilder encrpytedValue = new StringBuilder();
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var encrpytedBlock = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, string.Empty, DateTime.Now, DateTime.MaxValue, false, part));
|
||||
encrpytedValue.AppendLine(encrpytedBlock);
|
||||
}
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var encrpytedBlock = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, string.Empty, DateTime.Now, DateTime.MaxValue, false, part));
|
||||
encrpytedValue.AppendLine(encrpytedBlock);
|
||||
}
|
||||
|
||||
return encrpytedValue.ToString().TrimEnd();
|
||||
return encrpytedValue.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt the encrypted string using the Machine key in medium trust
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to be decrypted</param>
|
||||
/// <returns>The decrypted string.</returns>
|
||||
public static string DecryptWithMachineKey(this string value)
|
||||
/// Decrypt the encrypted string using the Machine key in medium trust
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to be decrypted</param>
|
||||
/// <returns>The decrypted string.</returns>
|
||||
public static string DecryptWithMachineKey(this string value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
string[] parts = value.Split('\n');
|
||||
string[] parts = value.Split('\n');
|
||||
|
||||
StringBuilder decryptedValue = new StringBuilder();
|
||||
StringBuilder decryptedValue = new StringBuilder();
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
decryptedValue.Append(FormsAuthentication.Decrypt(part.TrimEnd()).UserData);
|
||||
}
|
||||
foreach (var part in parts)
|
||||
{
|
||||
decryptedValue.Append(FormsAuthentication.Decrypt(part.TrimEnd()).UserData);
|
||||
}
|
||||
|
||||
return decryptedValue.ToString();
|
||||
return decryptedValue.ToString();
|
||||
}
|
||||
|
||||
|
||||
//this is from SqlMetal and just makes it a bit of fun to allow pluralisation
|
||||
public static string MakePluralName(this string name)
|
||||
{
|
||||
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("ss", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
|
||||
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("s", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
name = name + "es";
|
||||
return name;
|
||||
@@ -877,8 +879,8 @@ namespace Umbraco.Core
|
||||
{
|
||||
var helper = ShortStringHelper;
|
||||
var legacy = helper as LegacyShortStringHelper;
|
||||
return legacy != null
|
||||
? legacy.LegacyToUrlAlias(value, charReplacements, replaceDoubleDashes, stripNonAscii, urlEncode)
|
||||
return legacy != null
|
||||
? legacy.LegacyToUrlAlias(value, charReplacements, replaceDoubleDashes, stripNonAscii, urlEncode)
|
||||
: helper.CleanStringForUrlSegment(value);
|
||||
}
|
||||
|
||||
@@ -972,7 +974,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// the new methods to get a url segment
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Cleans a string to produce a string that can safely be used in an url segment.
|
||||
/// </summary>
|
||||
@@ -1110,7 +1112,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
return ShortStringHelper.CleanStringForSafeFileName(text, culture);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An extension method that returns a new string in which all occurrences of a
|
||||
/// specified string in the current instance are replaced with another specified string.
|
||||
@@ -1129,7 +1131,7 @@ namespace Umbraco.Core
|
||||
int index = -1 * newString.Length;
|
||||
|
||||
// Determine if there are any matches left in source, starting from just after the result of replacing the last match.
|
||||
while((index = source.IndexOf(oldString, index + newString.Length, stringComparison)) >= 0)
|
||||
while ((index = source.IndexOf(oldString, index + newString.Length, stringComparison)) >= 0)
|
||||
{
|
||||
// Remove the old text.
|
||||
source = source.Remove(index, oldString.Length);
|
||||
@@ -1200,5 +1202,28 @@ namespace Umbraco.Core
|
||||
var idCheckList = csv.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return idCheckList.Contains(value);
|
||||
}
|
||||
|
||||
// From: http://stackoverflow.com/a/961504/5018
|
||||
// filters control characters but allows only properly-formed surrogate sequences
|
||||
private static readonly Regex InvalidXmlChars =
|
||||
new Regex(
|
||||
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An extension method that returns a new string in which all occurrences of an
|
||||
/// unicode characters that are invalid in XML files are replaced with an empty string.
|
||||
/// </summary>
|
||||
/// <param name="text">Current instance of the string</param>
|
||||
/// <returns>Updated string</returns>
|
||||
///
|
||||
/// <summary>
|
||||
/// removes any unusual unicode characters that can't be encoded into XML
|
||||
/// </summary>
|
||||
internal static string ToValidXmlString(this string text)
|
||||
{
|
||||
return string.IsNullOrEmpty(text) ? text : InvalidXmlChars.Replace(text, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// The current status of the server in the Umbraco environment
|
||||
/// </summary>
|
||||
internal enum CurrentServerEnvironmentStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// If the current server is detected as the 'master' server when configured in a load balanced scenario
|
||||
/// </summary>
|
||||
Master,
|
||||
|
||||
/// <summary>
|
||||
/// If the current server is detected as a 'slave' server when configured in a load balanced scenario
|
||||
/// </summary>
|
||||
Slave,
|
||||
|
||||
/// <summary>
|
||||
/// If the current server cannot be detected as a 'slave' or 'master' when configured in a load balanced scenario
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// If load balancing is not enabled and this is the only server in the umbraco environment
|
||||
/// </summary>
|
||||
Single
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -19,18 +20,19 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
private readonly Func<Tuple<string, string>> _getUserNamePasswordDelegate;
|
||||
private volatile bool _hasResolvedDelegate = false;
|
||||
private readonly object _locker = new object();
|
||||
private bool _useDistributedCalls;
|
||||
private readonly object _locker = new object();
|
||||
|
||||
protected string Login { get; private set; }
|
||||
protected string Password{ get; private set; }
|
||||
|
||||
protected bool UseDistributedCalls { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Without a username/password all distribuion will be disabled
|
||||
/// </summary>
|
||||
internal DefaultServerMessenger()
|
||||
{
|
||||
_useDistributedCalls = false;
|
||||
UseDistributedCalls = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,7 +56,7 @@ namespace Umbraco.Core.Sync
|
||||
if (login == null) throw new ArgumentNullException("login");
|
||||
if (password == null) throw new ArgumentNullException("password");
|
||||
|
||||
_useDistributedCalls = useDistributedCalls;
|
||||
UseDistributedCalls = useDistributedCalls;
|
||||
Login = login;
|
||||
Password = password;
|
||||
}
|
||||
@@ -220,13 +222,13 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
Login = null;
|
||||
Password = null;
|
||||
_useDistributedCalls = false;
|
||||
UseDistributedCalls = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Login = result.Item1;
|
||||
Password = result.Item2;
|
||||
_useDistributedCalls = UmbracoSettings.UseDistributedCalls;
|
||||
UseDistributedCalls = UmbracoSettings.UseDistributedCalls;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -234,7 +236,7 @@ namespace Umbraco.Core.Sync
|
||||
LogHelper.Error<DefaultServerMessenger>("Could not resolve username/password delegate, server distribution will be disabled", ex);
|
||||
Login = null;
|
||||
Password = null;
|
||||
_useDistributedCalls = false;
|
||||
UseDistributedCalls = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,7 +315,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
|
||||
// can definitely not distribute.
|
||||
if (!_useDistributedCalls || !servers.Any())
|
||||
if (!UseDistributedCalls || !servers.Any())
|
||||
{
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
InvokeMethodOnRefresherInstance(refresher, dispatchType, getId, instances);
|
||||
@@ -324,7 +326,7 @@ namespace Umbraco.Core.Sync
|
||||
MessageSeversForIdsOrJson(servers, refresher, dispatchType, instances.Select(getId));
|
||||
}
|
||||
|
||||
private void MessageSeversForIdsOrJson(
|
||||
protected virtual void MessageSeversForIdsOrJson(
|
||||
IEnumerable<IServerAddress> servers,
|
||||
ICacheRefresher refresher,
|
||||
MessageType dispatchType,
|
||||
@@ -344,7 +346,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
|
||||
// can definitely not distribute.
|
||||
if (!_useDistributedCalls || !servers.Any())
|
||||
if (!UseDistributedCalls || !servers.Any())
|
||||
{
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload);
|
||||
@@ -455,16 +457,16 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
}
|
||||
|
||||
List<WaitHandle> waitHandlesList;
|
||||
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
|
||||
|
||||
var waitHandlesList = asyncResultsList.Select(x => x.AsyncWaitHandle).ToArray();
|
||||
|
||||
var errorCount = 0;
|
||||
|
||||
// Once for each WaitHandle that we have, wait for a response and log it
|
||||
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
|
||||
foreach (var t in asyncResults)
|
||||
//Wait for all requests to complete
|
||||
WaitHandle.WaitAll(waitHandlesList.ToArray());
|
||||
|
||||
foreach (var t in asyncResultsList)
|
||||
{
|
||||
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
|
||||
//var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
|
||||
|
||||
try
|
||||
{
|
||||
@@ -519,18 +521,7 @@ namespace Umbraco.Core.Sync
|
||||
LogDispatchBatchError(ee);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
|
||||
{
|
||||
var asyncResults = asyncResultsList.ToArray();
|
||||
waitHandlesList = new List<WaitHandle>();
|
||||
foreach (var asyncResult in asyncResults)
|
||||
{
|
||||
waitHandlesList.Add(asyncResult.AsyncWaitHandle);
|
||||
}
|
||||
return asyncResults;
|
||||
}
|
||||
|
||||
|
||||
private void LogDispatchBatchError(Exception ee)
|
||||
{
|
||||
LogHelper.Error<DefaultServerMessenger>("Error refreshing distributed list", ee);
|
||||
|
||||
@@ -8,5 +8,7 @@ namespace Umbraco.Core.Sync
|
||||
internal interface IServerAddress
|
||||
{
|
||||
string ServerAddress { get; }
|
||||
|
||||
//TODO : Should probably add things like port, protocol, server name, app id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
[Serializable]
|
||||
public class RefreshInstruction
|
||||
{
|
||||
public RefreshMethodType RefreshType { get; set; }
|
||||
public Guid RefresherId { get; set; }
|
||||
public Guid GuidId { get; set; }
|
||||
public int IntId { get; set; }
|
||||
public string JsonIds { get; set; }
|
||||
public string JsonPayload { get; set; }
|
||||
|
||||
[Serializable]
|
||||
public enum RefreshMethodType
|
||||
{
|
||||
RefreshAll,
|
||||
RefreshByGuid,
|
||||
RefreshById,
|
||||
RefreshByIds,
|
||||
RefreshByJson,
|
||||
RemoveById
|
||||
}
|
||||
|
||||
protected bool Equals(RefreshInstruction other)
|
||||
{
|
||||
return RefreshType == other.RefreshType && RefresherId.Equals(other.RefresherId) && GuidId.Equals(other.GuidId) && IntId == other.IntId && string.Equals(JsonIds, other.JsonIds) && string.Equals(JsonPayload, other.JsonPayload);
|
||||
}
|
||||
|
||||
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((RefreshInstruction) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = (int) RefreshType;
|
||||
hashCode = (hashCode*397) ^ RefresherId.GetHashCode();
|
||||
hashCode = (hashCode*397) ^ GuidId.GetHashCode();
|
||||
hashCode = (hashCode*397) ^ IntId;
|
||||
hashCode = (hashCode*397) ^ (JsonIds != null ? JsonIds.GetHashCode() : 0);
|
||||
hashCode = (hashCode*397) ^ (JsonPayload != null ? JsonPayload.GetHashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(RefreshInstruction left, RefreshInstruction right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RefreshInstruction left, RefreshInstruction right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper used to determine the current server environment status
|
||||
/// </summary>
|
||||
internal static class ServerEnvironmentHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the current umbraco base url for the current server depending on it's environment
|
||||
/// status. This will attempt to determine the internal umbraco base url that can be used by the current
|
||||
/// server to send a request to itself if it is in a load balanced environment.
|
||||
/// </summary>
|
||||
/// <returns>The full base url including schema (i.e. http://myserver:80/umbraco )</returns>
|
||||
public static string GetCurrentServerUmbracoBaseUrl()
|
||||
{
|
||||
var status = GetStatus();
|
||||
|
||||
if (status == CurrentServerEnvironmentStatus.Single)
|
||||
{
|
||||
//if it's a single install, then the base url has to be the first url registered
|
||||
return string.Format("http://{0}", ApplicationContext.Current.OriginalRequestUrl);
|
||||
}
|
||||
|
||||
var servers = UmbracoSettings.DistributionServers;
|
||||
|
||||
var nodes = servers.SelectNodes("./server");
|
||||
if (nodes == null)
|
||||
{
|
||||
//cannot be determined, then the base url has to be the first url registered
|
||||
return string.Format("http://{0}", ApplicationContext.Current.OriginalRequestUrl);
|
||||
}
|
||||
|
||||
var xmlNodes = nodes.Cast<XmlNode>().ToList();
|
||||
|
||||
foreach (var xmlNode in xmlNodes)
|
||||
{
|
||||
var appId = xmlNode.AttributeValue<string>("appId");
|
||||
var serverName = xmlNode.AttributeValue<string>("serverName");
|
||||
|
||||
if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId))
|
||||
|| (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)))
|
||||
{
|
||||
//match by appId or computer name! return the url configured
|
||||
return string.Format("{0}://{1}:{2}/{3}",
|
||||
xmlNode.AttributeValue<string>("forceProtocol").IsNullOrWhiteSpace() ? "http" : xmlNode.AttributeValue<string>("forceProtocol"),
|
||||
xmlNode.InnerText,
|
||||
xmlNode.AttributeValue<string>("forcePortnumber").IsNullOrWhiteSpace() ? "80" : xmlNode.AttributeValue<string>("forcePortnumber"),
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco).TrimStart('/'));
|
||||
}
|
||||
}
|
||||
|
||||
//cannot be determined, then the base url has to be the first url registered
|
||||
return string.Format("http://{0}", ApplicationContext.Current.OriginalRequestUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current environment status for the current server
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CurrentServerEnvironmentStatus GetStatus()
|
||||
{
|
||||
if (UmbracoSettings.UseDistributedCalls == false)
|
||||
{
|
||||
return CurrentServerEnvironmentStatus.Single;
|
||||
}
|
||||
|
||||
var servers = UmbracoSettings.DistributionServers;
|
||||
|
||||
var nodes = servers.SelectNodes("./server");
|
||||
if (nodes == null)
|
||||
{
|
||||
return CurrentServerEnvironmentStatus.Unknown;
|
||||
}
|
||||
|
||||
var master = nodes.Cast<XmlNode>().FirstOrDefault();
|
||||
|
||||
if (master == null)
|
||||
{
|
||||
return CurrentServerEnvironmentStatus.Unknown;
|
||||
}
|
||||
|
||||
//we determine master/slave based on the first server registered
|
||||
//TODO: In v7 we have publicized ServerRegisterResolver - we won't be able to determine this based on that
|
||||
// but we'd need to change the IServerAddress interfaces which is breaking.
|
||||
|
||||
var appId = master.AttributeValue<string>("appId");
|
||||
var serverName = master.AttributeValue<string>("serverName");
|
||||
|
||||
if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace())
|
||||
{
|
||||
return CurrentServerEnvironmentStatus.Unknown;
|
||||
}
|
||||
|
||||
if ((appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId))
|
||||
|| (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)))
|
||||
{
|
||||
//match by appdid or server name!
|
||||
return CurrentServerEnvironmentStatus.Master;
|
||||
}
|
||||
|
||||
return CurrentServerEnvironmentStatus.Slave;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The client Soap service for making distrubuted cache calls between servers
|
||||
/// </summary>
|
||||
@@ -21,6 +20,33 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/BulkRefresh", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void BulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password)
|
||||
{
|
||||
this.Invoke("BulkRefresh", new object[] {
|
||||
instructions,
|
||||
appId,
|
||||
login,
|
||||
password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginBulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("BulkRefresh", new object[] {
|
||||
instructions,
|
||||
appId,
|
||||
login,
|
||||
password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndBulkRefresh(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshAll(System.Guid uniqueIdentifier, string Login, string Password)
|
||||
|
||||
@@ -82,7 +82,9 @@
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Services" />
|
||||
<Reference Include="System.Web.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll</HintPath>
|
||||
@@ -121,8 +123,6 @@
|
||||
<Compile Include="Cache\CacheRefresherBase.cs" />
|
||||
<Compile Include="Cache\CacheRefresherEventArgs.cs" />
|
||||
<Compile Include="Cache\DictionaryCacheProviderBase.cs" />
|
||||
<Compile Include="Cache\DictionaryCacheWrapper.cs" />
|
||||
<Compile Include="Cache\DictionaryItemWrapper.cs" />
|
||||
<Compile Include="Cache\HttpRequestCacheProvider.cs" />
|
||||
<Compile Include="Cache\HttpRuntimeCacheProvider.cs" />
|
||||
<Compile Include="Cache\ICacheProvider.cs" />
|
||||
@@ -217,6 +217,7 @@
|
||||
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
|
||||
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
|
||||
<Compile Include="Models\TemplateNode.cs" />
|
||||
<Compile Include="MonitorLock.cs" />
|
||||
<Compile Include="Packaging\Models\InstallAction.cs" />
|
||||
<Compile Include="Packaging\Models\InstallationSummary.cs" />
|
||||
<Compile Include="Packaging\Models\MetaData.cs" />
|
||||
@@ -770,6 +771,7 @@
|
||||
<Compile Include="Publishing\PublishingStrategy.cs" />
|
||||
<Compile Include="Publishing\PublishStatus.cs" />
|
||||
<Compile Include="Publishing\PublishStatusType.cs" />
|
||||
<Compile Include="Publishing\ScheduledPublisher.cs" />
|
||||
<Compile Include="RenderingEngine.cs" />
|
||||
<Compile Include="Security\AuthenticationExtensions.cs" />
|
||||
<Compile Include="Security\IUsersMembershipProvider.cs" />
|
||||
@@ -830,6 +832,9 @@
|
||||
<Compile Include="Standalone\StandaloneCoreBootManager.cs" />
|
||||
<Compile Include="Strings\ContentBaseExtensions.cs" />
|
||||
<Compile Include="Strings\Diff.cs" />
|
||||
<Compile Include="Sync\CurrentServerEnvironmentStatus.cs" />
|
||||
<Compile Include="Sync\RefreshInstruction.cs" />
|
||||
<Compile Include="Sync\ServerEnvironmentHelper.cs" />
|
||||
<Compile Include="TopologicalSorter.cs" />
|
||||
<Compile Include="Strings\DefaultUrlSegmentProvider.cs" />
|
||||
<Compile Include="Strings\IUrlSegmentProvider.cs" />
|
||||
@@ -924,6 +929,7 @@
|
||||
<Compile Include="Xml\XPathVariable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a performance tweak to check if this is a .css, .js or .ico, .jpg, .jpeg, .png, .gif file request since
|
||||
/// This is a performance tweak to check if this not an ASP.Net server file
|
||||
/// .Net will pass these requests through to the module when in integrated mode.
|
||||
/// We want to ignore all of these requests immediately.
|
||||
/// </summary>
|
||||
@@ -142,10 +142,10 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
internal static bool IsClientSideRequest(this Uri url)
|
||||
{
|
||||
// fixme - IsClientSideRequest should not use an hard-coded list of extensions
|
||||
// a client-side request is anything that has an extension that is not .aspx?
|
||||
var toIgnore = new[] { ".js", ".css", ".ico", ".png", ".jpg", ".jpeg", ".gif", ".html", ".svg" };
|
||||
return toIgnore.Any(x => Path.GetExtension(url.LocalPath).InvariantEquals(x));
|
||||
var ext = Path.GetExtension(url.LocalPath);
|
||||
if (ext.IsNullOrWhiteSpace()) return false;
|
||||
var toInclude = new[] { ".aspx", ".ashx", ".asmx", ".axd", ".svc" };
|
||||
return toInclude.Any(ext.InvariantEquals) == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -121,6 +121,14 @@
|
||||
|
||||
</dependentAssembly>
|
||||
|
||||
<dependentAssembly>
|
||||
|
||||
<assemblyIdentity name="NuGet.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.0.11220.104" newVersion="1.0.11220.104" />
|
||||
|
||||
</dependentAssembly>
|
||||
|
||||
</assemblyBinding>
|
||||
|
||||
</runtime>
|
||||
|
||||
@@ -15,6 +15,8 @@ using log4net.Repository;
|
||||
|
||||
namespace Umbraco.Tests
|
||||
{
|
||||
//Ignore this test, it fails sometimes on the build server - pretty sure it's a threading issue with this test class
|
||||
[Ignore]
|
||||
[TestFixture]
|
||||
public class AsynchronousRollingFileAppenderTests
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
base.Setup();
|
||||
_ctx = new FakeHttpContextFactory("http://localhost/test");
|
||||
_provider = new HttpRequestCacheProvider(() => _ctx.HttpContext);
|
||||
_provider = new HttpRequestCacheProvider(_ctx.HttpContext);
|
||||
}
|
||||
|
||||
internal override ICacheProvider Provider
|
||||
|
||||
@@ -51,24 +51,20 @@ namespace Umbraco.Tests.Membership
|
||||
{
|
||||
var date = DateTime.Now;
|
||||
|
||||
var m = Mock.Of<MembershipUser>(
|
||||
user => user.UserName == "test username"
|
||||
&& user.Comment == "test comment"
|
||||
&& user.IsApproved == true
|
||||
&& user.IsLockedOut == false
|
||||
&& user.CreationDate == date
|
||||
&& user.Email == "test@email.com"
|
||||
&& user.LastActivityDate == date.AddMinutes(1)
|
||||
&& user.LastLockoutDate == date.AddMinutes(2)
|
||||
&& user.LastLoginDate == date.AddMinutes(3)
|
||||
&& user.LastPasswordChangedDate == date.AddMinutes(4)
|
||||
&& user.PasswordQuestion == "test question");
|
||||
var member = new Member("test name", "test@email.com", "test username", "test password",
|
||||
GetMemberType());
|
||||
member.Comments = "test comment";
|
||||
member.IsApproved = true;
|
||||
member.IsLockedOut = false;
|
||||
member.CreateDate = date;
|
||||
member.LastLoginDate = date.AddMinutes(1);
|
||||
member.LastLockoutDate = date.AddMinutes(2);
|
||||
//NOTE: Last activity date is always the same as last login date since we don't have a place to store that data
|
||||
//member.LastLoginDate = date.AddMinutes(3);
|
||||
member.LastPasswordChangeDate = date.AddMinutes(4);
|
||||
member.PasswordQuestion = "test question";
|
||||
|
||||
|
||||
var mpc = new MemberPublishedContent(
|
||||
new Member("test name", "test@email.com", "test username", "test password",
|
||||
Mock.Of<IMemberType>(type => type.Alias == "Member")),
|
||||
m);
|
||||
var mpc = new MemberPublishedContent(member);
|
||||
|
||||
var d = mpc.AsDynamic();
|
||||
|
||||
@@ -79,7 +75,7 @@ namespace Umbraco.Tests.Membership
|
||||
Assert.AreEqual(false, d.IsLockedOut);
|
||||
Assert.AreEqual(date.AddMinutes(1), d.LastActivityDate);
|
||||
Assert.AreEqual(date.AddMinutes(2), d.LastLockoutDate);
|
||||
Assert.AreEqual(date.AddMinutes(3), d.LastLoginDate);
|
||||
Assert.AreEqual(date.AddMinutes(1), d.LastLoginDate);
|
||||
Assert.AreEqual(date.AddMinutes(4), d.LastPasswordChangedDate);
|
||||
Assert.AreEqual("test name", d.Name);
|
||||
Assert.AreEqual("test question", d.PasswordQuestion);
|
||||
@@ -92,24 +88,20 @@ namespace Umbraco.Tests.Membership
|
||||
{
|
||||
var date = DateTime.Now;
|
||||
|
||||
var m = Mock.Of<MembershipUser>(
|
||||
user => user.UserName == "test username"
|
||||
&& user.Comment == "test comment"
|
||||
&& user.IsApproved == true
|
||||
&& user.IsLockedOut == false
|
||||
&& user.CreationDate == date
|
||||
&& user.Email == "test@email.com"
|
||||
&& user.LastActivityDate == date.AddMinutes(1)
|
||||
&& user.LastLockoutDate == date.AddMinutes(2)
|
||||
&& user.LastLoginDate == date.AddMinutes(3)
|
||||
&& user.LastPasswordChangedDate == date.AddMinutes(4)
|
||||
&& user.PasswordQuestion == "test question");
|
||||
var member = new Member("test name", "test@email.com", "test username", "test password",
|
||||
GetMemberType());
|
||||
member.Comments = "test comment";
|
||||
member.IsApproved = true;
|
||||
member.IsLockedOut = false;
|
||||
member.CreateDate = date;
|
||||
member.LastLoginDate = date.AddMinutes(1);
|
||||
member.LastLockoutDate = date.AddMinutes(2);
|
||||
//NOTE: Last activity date is always the same as last login date since we don't have a place to store that data
|
||||
//member.LastLoginDate = date.AddMinutes(3);
|
||||
member.LastPasswordChangeDate = date.AddMinutes(4);
|
||||
member.PasswordQuestion = "test question";
|
||||
|
||||
|
||||
var mpc = new MemberPublishedContent(
|
||||
new Member("test name", "test@email.com", "test username", "test password",
|
||||
Mock.Of<IMemberType>(type => type.Alias == "Member")) ,
|
||||
m);
|
||||
var mpc = new MemberPublishedContent(member);
|
||||
|
||||
var d = mpc.AsDynamic();
|
||||
|
||||
@@ -120,7 +112,7 @@ namespace Umbraco.Tests.Membership
|
||||
Assert.AreEqual(false, d.isLockedOut);
|
||||
Assert.AreEqual(date.AddMinutes(1), d.lastActivityDate);
|
||||
Assert.AreEqual(date.AddMinutes(2), d.lastLockoutDate);
|
||||
Assert.AreEqual(date.AddMinutes(3), d.lastLoginDate);
|
||||
Assert.AreEqual(date.AddMinutes(1), d.lastLoginDate);
|
||||
Assert.AreEqual(date.AddMinutes(4), d.lastPasswordChangedDate);
|
||||
Assert.AreEqual("test name", d.name);
|
||||
Assert.AreEqual("test question", d.passwordQuestion);
|
||||
@@ -133,25 +125,23 @@ namespace Umbraco.Tests.Membership
|
||||
{
|
||||
var date = DateTime.Now;
|
||||
|
||||
var m = Mock.Of<MembershipUser>(
|
||||
user => user.UserName == "test username"
|
||||
&& user.Comment == "test comment"
|
||||
&& user.IsApproved == true
|
||||
&& user.IsLockedOut == false
|
||||
&& user.CreationDate == date
|
||||
&& user.Email == "test@email.com"
|
||||
&& user.LastActivityDate == date.AddMinutes(1)
|
||||
&& user.LastLockoutDate == date.AddMinutes(2)
|
||||
&& user.LastLoginDate == date.AddMinutes(3)
|
||||
&& user.LastPasswordChangedDate == date.AddMinutes(4)
|
||||
&& user.PasswordQuestion == "test question");
|
||||
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType("Member", "Member");
|
||||
var member = MockedMember.CreateSimpleMember(memberType, "test name", "test@email.com", "test password", "test username");
|
||||
member.Comments = "test comment";
|
||||
member.IsApproved = true;
|
||||
member.IsLockedOut = false;
|
||||
member.CreateDate = date;
|
||||
member.LastLoginDate = date.AddMinutes(1);
|
||||
member.LastLockoutDate = date.AddMinutes(2);
|
||||
//NOTE: Last activity date is always the same as last login date since we don't have a place to store that data
|
||||
//member.LastLoginDate = date.AddMinutes(3);
|
||||
member.LastPasswordChangeDate = date.AddMinutes(4);
|
||||
member.PasswordQuestion = "test question";
|
||||
|
||||
member.Properties["title"].Value = "Test Value 1";
|
||||
member.Properties["bodyText"].Value = "Test Value 2";
|
||||
member.Properties["author"].Value = "Test Value 3";
|
||||
var mpc = new MemberPublishedContent(member, m);
|
||||
var mpc = new MemberPublishedContent(member);
|
||||
|
||||
var d = mpc.AsDynamic();
|
||||
|
||||
@@ -165,6 +155,21 @@ namespace Umbraco.Tests.Membership
|
||||
|
||||
}
|
||||
|
||||
private IMemberType GetMemberType()
|
||||
{
|
||||
var entity = new MemberType(-1)
|
||||
{
|
||||
Alias = "Member"
|
||||
};
|
||||
|
||||
entity.AddPropertyGroup(Umbraco.Core.Constants.Conventions.Member.StandardPropertiesGroupName);
|
||||
var standardPropertyTypes = Umbraco.Core.Constants.Conventions.Member.GetStandardPropertyTypeStubs();
|
||||
foreach (var standardPropertyType in standardPropertyTypes)
|
||||
{
|
||||
entity.AddPropertyType(standardPropertyType.Value, Umbraco.Core.Constants.Conventions.Member.StandardPropertiesGroupName);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Umbraco.Tests.Membership
|
||||
Assert.AreEqual(false, provider.EnablePasswordReset);
|
||||
Assert.AreEqual(false, provider.RequiresQuestionAndAnswer);
|
||||
Assert.AreEqual(true, provider.RequiresUniqueEmail);
|
||||
Assert.AreEqual(5, provider.MaxInvalidPasswordAttempts);
|
||||
Assert.AreEqual(20, provider.MaxInvalidPasswordAttempts);
|
||||
Assert.AreEqual(10, provider.PasswordAttemptWindow);
|
||||
Assert.AreEqual(provider.DefaultMinPasswordLength, provider.MinRequiredPasswordLength);
|
||||
Assert.AreEqual(provider.DefaultMinNonAlphanumericChars, provider.MinRequiredNonAlphanumericCharacters);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using SQLCE4Umbraco;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
@@ -13,13 +17,20 @@ using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
|
||||
namespace Umbraco.Tests.Migrations
|
||||
{
|
||||
[TestFixture]
|
||||
public class TargetVersionSixthMigrationsTest
|
||||
public class TargetVersionSixthMigrationsTest : BaseDatabaseFactoryTest
|
||||
{
|
||||
/// <summary>Regular expression that finds multiline block comments.</summary>
|
||||
private static readonly Regex FindComments = new Regex(@"\/\*.*?\*\/", RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
|
||||
[SetUp]
|
||||
public void Initialize()
|
||||
public override void Initialize()
|
||||
{
|
||||
TestHelper.SetupLog4NetForTests();
|
||||
TestHelper.InitializeContentDirectories();
|
||||
|
||||
Path = TestHelper.CurrentAssemblyDirectory;
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", Path);
|
||||
|
||||
MigrationResolver.Current = new MigrationResolver(() => new List<Type>
|
||||
{
|
||||
typeof (Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero.RemoveUmbracoAppConstraints),
|
||||
@@ -36,22 +47,39 @@ namespace Umbraco.Tests.Migrations
|
||||
typeof (UpdateCmsPropertyTypeGroupTable)
|
||||
}.OrderByDescending(x => x.Name));
|
||||
|
||||
Resolution.Freeze();
|
||||
Resolution.Freeze();
|
||||
|
||||
SqlSyntaxContext.SqlSyntaxProvider = SqlCeSyntax.Provider;
|
||||
|
||||
var engine = new SqlCeEngine("Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf;Flush Interval=1;");
|
||||
engine.CreateDatabase();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Can_Find_Targetted_Migrations()
|
||||
{
|
||||
var db = GetConfiguredDatabase();
|
||||
|
||||
//Create db schema and data from old Total.sql file for Sql Ce
|
||||
string statements = GetDatabaseSpecificSqlScript();
|
||||
// replace block comments by whitespace
|
||||
statements = FindComments.Replace(statements, " ");
|
||||
// execute all non-empty statements
|
||||
foreach (string statement in statements.Split(";".ToCharArray()))
|
||||
{
|
||||
string rawStatement = statement.Replace("GO", "").Trim();
|
||||
if (rawStatement.Length > 0)
|
||||
db.Execute(new Sql(rawStatement));
|
||||
}
|
||||
|
||||
var configuredVersion = new Version("4.8.0");
|
||||
var targetVersion = new Version("6.0.0");
|
||||
var foundMigrations = MigrationResolver.Current.Migrations;
|
||||
var foundMigrations = MigrationResolver.Current.Migrations;
|
||||
|
||||
var migrationRunner = new MigrationRunner(configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName);
|
||||
var migrations = migrationRunner.OrderedUpgradeMigrations(foundMigrations).ToList();
|
||||
|
||||
var context = new MigrationContext(DatabaseProviders.SqlServerCE, null);
|
||||
var context = new MigrationContext(DatabaseProviders.SqlServerCE, db);
|
||||
foreach (MigrationBase migration in migrations)
|
||||
{
|
||||
migration.GetUpExpressions(context);
|
||||
@@ -66,9 +94,37 @@ namespace Umbraco.Tests.Migrations
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
|
||||
PluginManager.Current = null;
|
||||
SqlSyntaxContext.SqlSyntaxProvider = null;
|
||||
MigrationResolver.Reset();
|
||||
|
||||
TestHelper.CleanContentDirectories();
|
||||
|
||||
Path = TestHelper.CurrentAssemblyDirectory;
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", null);
|
||||
|
||||
SqlCeContextGuardian.CloseBackgroundConnection();
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public UmbracoDatabase GetConfiguredDatabase()
|
||||
{
|
||||
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf;Flush Interval=1;", "System.Data.SqlServerCe.4.0");
|
||||
}
|
||||
|
||||
public DatabaseProviders GetDatabaseProvider()
|
||||
{
|
||||
return DatabaseProviders.SqlServerCE;
|
||||
}
|
||||
|
||||
public string GetDatabaseSpecificSqlScript()
|
||||
{
|
||||
return SqlScripts.SqlResources.SqlCeTotal_480;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +162,7 @@ namespace Umbraco.Tests.Models
|
||||
}
|
||||
Assert.AreNotSame(clone.PropertyTypes, contentType.PropertyTypes);
|
||||
Assert.AreEqual(clone.PropertyTypes.Count(), contentType.PropertyTypes.Count());
|
||||
Assert.AreEqual(0, ((ContentTypeBase)clone).NonGroupedPropertyTypes.Count());
|
||||
for (var index = 0; index < contentType.PropertyTypes.Count(); index++)
|
||||
{
|
||||
Assert.AreNotSame(clone.PropertyTypes.ElementAt(index), contentType.PropertyTypes.ElementAt(index));
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class PreValueCollectionTests
|
||||
{
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
var d = new PreValueCollection(new Dictionary<string, PreValue>
|
||||
{
|
||||
{"blah1", new PreValue(1, "test1", 1)},
|
||||
{"blah2", new PreValue(2, "test1", 3)},
|
||||
{"blah3", new PreValue(3, "test1", 2)}
|
||||
});
|
||||
|
||||
var a = new PreValueCollection(new[]
|
||||
{
|
||||
new PreValue(1, "test1", 1),
|
||||
new PreValue(2, "test1", 3),
|
||||
new PreValue(3, "test1", 2)
|
||||
});
|
||||
|
||||
var clone1 = (PreValueCollection)d.DeepClone();
|
||||
var clone2 = (PreValueCollection)a.DeepClone();
|
||||
|
||||
Action<PreValueCollection, PreValueCollection> assert = (orig, clone) =>
|
||||
{
|
||||
Assert.AreNotSame(orig, clone);
|
||||
var oDic = orig.FormatAsDictionary();
|
||||
var cDic = clone.FormatAsDictionary();
|
||||
Assert.AreEqual(oDic.Keys.Count(), cDic.Keys.Count());
|
||||
foreach (var k in oDic.Keys)
|
||||
{
|
||||
Assert.AreNotSame(oDic[k], cDic[k]);
|
||||
Assert.AreEqual(oDic[k].Id, cDic[k].Id);
|
||||
Assert.AreEqual(oDic[k].SortOrder, cDic[k].SortOrder);
|
||||
Assert.AreEqual(oDic[k].Value, cDic[k].Value);
|
||||
}
|
||||
};
|
||||
|
||||
assert(d, clone1);
|
||||
assert(a, clone2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System.CodeDom;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Tests.Mvc
|
||||
{
|
||||
[TestFixture]
|
||||
public class SurfaceControllerTests
|
||||
{
|
||||
[Test]
|
||||
public void Can_Construct_And_Get_Result()
|
||||
{
|
||||
var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper());
|
||||
ApplicationContext.EnsureContext(appCtx, true);
|
||||
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
new Mock<HttpContextBase>().Object,
|
||||
appCtx,
|
||||
true);
|
||||
|
||||
var ctrl = new TestSurfaceController(umbCtx);
|
||||
|
||||
var result = ctrl.Index();
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Umbraco_Context_Not_Null()
|
||||
{
|
||||
var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper());
|
||||
ApplicationContext.EnsureContext(appCtx, true);
|
||||
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
new Mock<HttpContextBase>().Object,
|
||||
appCtx,
|
||||
true);
|
||||
|
||||
var ctrl = new TestSurfaceController(umbCtx);
|
||||
|
||||
Assert.IsNotNull(ctrl.UmbracoContext);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Umbraco_Helper_Not_Null()
|
||||
{
|
||||
var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper());
|
||||
ApplicationContext.EnsureContext(appCtx, true);
|
||||
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
new Mock<HttpContextBase>().Object,
|
||||
appCtx,
|
||||
true);
|
||||
|
||||
var ctrl = new TestSurfaceController(umbCtx);
|
||||
|
||||
Assert.IsNotNull(ctrl.Umbraco);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Lookup_Content()
|
||||
{
|
||||
//init app context
|
||||
|
||||
var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper());
|
||||
|
||||
//TODO: Need to either make this public or make all methods on the UmbracoHelper or
|
||||
// in v7 the PublishedContentQuery object virtual so we can just mock the methods
|
||||
|
||||
var contentCaches = new Mock<IPublishedCaches>();
|
||||
|
||||
//init content resolver
|
||||
//TODO: This is not public so people cannot actually do this!
|
||||
|
||||
PublishedCachesResolver.Current = new PublishedCachesResolver(contentCaches.Object);
|
||||
|
||||
//init umb context
|
||||
|
||||
var umbCtx = UmbracoContext.EnsureContext(
|
||||
new Mock<HttpContextBase>().Object,
|
||||
appCtx,
|
||||
true);
|
||||
|
||||
//setup the mock
|
||||
|
||||
contentCaches.Setup(caches => caches.CreateContextualContentCache(It.IsAny<UmbracoContext>()))
|
||||
.Returns(new ContextualPublishedContentCache(
|
||||
Mock.Of<IPublishedContentCache>(cache =>
|
||||
cache.GetById(It.IsAny<UmbracoContext>(), false, It.IsAny<int>()) ==
|
||||
//return mock of IPublishedContent for any call to GetById
|
||||
Mock.Of<IPublishedContent>(content => content.Id == 2)),
|
||||
umbCtx));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using (var uTest = new DisposableUmbracoTest(appCtx))
|
||||
{
|
||||
var ctrl = new TestSurfaceController(uTest.UmbracoContext);
|
||||
var result = ctrl.GetContent(2) as PublishedContentResult;
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(2, result.Content.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestSurfaceController : SurfaceController
|
||||
{
|
||||
public TestSurfaceController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ActionResult GetContent(int id)
|
||||
{
|
||||
var content = Umbraco.TypedContent(id);
|
||||
|
||||
return new PublishedContentResult(content);
|
||||
}
|
||||
}
|
||||
|
||||
public class PublishedContentResult : ActionResult
|
||||
{
|
||||
public IPublishedContent Content { get; set; }
|
||||
|
||||
public PublishedContentResult(IPublishedContent content)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
@@ -10,6 +11,7 @@ using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
@@ -37,10 +39,13 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
|
||||
// Act
|
||||
var repository = RepositoryResolver.Current.ResolveByType<ITemplateRepository>(unitOfWork);
|
||||
using (var repository = RepositoryResolver.Current.ResolveByType<ITemplateRepository>(unitOfWork))
|
||||
{
|
||||
|
||||
// Assert
|
||||
Assert.That(repository, Is.Not.Null);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.That(repository, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -51,10 +56,13 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
|
||||
// Act
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
|
||||
// Assert
|
||||
Assert.That(repository, Is.Not.Null);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.That(repository, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -63,16 +71,18 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
// Act
|
||||
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
//Assert
|
||||
Assert.That(repository.Get("test"), Is.Not.Null);
|
||||
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
|
||||
//Assert
|
||||
Assert.That(repository.Get("test"), Is.Not.Null);
|
||||
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,22 +91,25 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
// Act
|
||||
var template = new Template("test-updated-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var template = new Template("test-updated-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
template.Content = @"<%@ Master Language=""VB"" %>";
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
template.Content = @"<%@ Master Language=""VB"" %>";
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
var updated = repository.Get("test");
|
||||
|
||||
var updated = repository.Get("test");
|
||||
// Assert
|
||||
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
|
||||
Assert.That(updated.Content, Is.EqualTo(@"<%@ Master Language=""VB"" %>"));
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
|
||||
Assert.That(updated.Content, Is.EqualTo(@"<%@ Master Language=""VB"" %>"));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -105,19 +118,63 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
// Act
|
||||
var templates = repository.Get("test");
|
||||
repository.Delete(templates);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var templates = repository.Get("test");
|
||||
repository.Delete(templates);
|
||||
unitOfWork.Commit();
|
||||
// Assert
|
||||
Assert.IsNull(repository.Get("test"));
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(repository.Get("test"));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Delete_When_Assigned_To_Doc()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
|
||||
var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current);
|
||||
var contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository);
|
||||
var contentRepo = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, CacheHelper.CreateDisabledCacheHelper());
|
||||
|
||||
using (contentRepo)
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage");
|
||||
var textpage = MockedContent.CreateSimpleContent(contentType);
|
||||
contentTypeRepository.AddOrUpdate(contentType);
|
||||
contentRepo.AddOrUpdate(textpage);
|
||||
unitOfWork.Commit();
|
||||
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
repository.AddOrUpdate(template);
|
||||
unitOfWork.Commit();
|
||||
|
||||
textpage.Template = template;
|
||||
contentRepo.AddOrUpdate(textpage);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var templates = repository.Get("test");
|
||||
repository.Delete(templates);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(repository.Get("test"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -126,27 +183,30 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
|
||||
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var child = new Template("test-child-masterpage.master", "child", "child") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var child = new Template("test-child-masterpage.master", "child", "child") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var baby = new Template("test-baby-masterpage.master", "baby", "baby") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
child.MasterTemplateAlias = parent.Alias;
|
||||
child.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
baby.MasterTemplateAlias = child.Alias;
|
||||
baby.MasterTemplateId = new Lazy<int>(() => child.Id);
|
||||
repository.AddOrUpdate(parent);
|
||||
repository.AddOrUpdate(child);
|
||||
repository.AddOrUpdate(baby);
|
||||
unitOfWork.Commit();
|
||||
child.MasterTemplateAlias = parent.Alias;
|
||||
child.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
baby.MasterTemplateAlias = child.Alias;
|
||||
baby.MasterTemplateId = new Lazy<int>(() => child.Id);
|
||||
repository.AddOrUpdate(parent);
|
||||
repository.AddOrUpdate(child);
|
||||
repository.AddOrUpdate(baby);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var templates = repository.Get("parent");
|
||||
repository.Delete(templates);
|
||||
unitOfWork.Commit();
|
||||
// Act
|
||||
var templates = repository.Get("parent");
|
||||
repository.Delete(templates);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(repository.Get("test"));
|
||||
// Assert
|
||||
Assert.IsNull(repository.Get("test"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,66 +215,69 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
// Arrange
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var unitOfWork = provider.GetUnitOfWork();
|
||||
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
|
||||
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
|
||||
{
|
||||
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
|
||||
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var child1 = new Template("test-child1-masterpage.master", "child1", "child1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler1 = new Template("test-toddler1-masterpage.master", "toddler1", "toddler1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler2 = new Template("test-toddler2-masterpage.master", "toddler2", "toddler2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var baby1 = new Template("test-baby1-masterpage.master", "baby1", "baby1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
|
||||
var child1 = new Template("test-child1-masterpage.master", "child1", "child1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler1 = new Template("test-toddler1-masterpage.master", "toddler1", "toddler1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler2 = new Template("test-toddler2-masterpage.master", "toddler2", "toddler2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var baby1 = new Template("test-baby1-masterpage.master", "baby1", "baby1") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
|
||||
var child2 = new Template("test-child2-masterpage.master", "child2", "child2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler3 = new Template("test-toddler3-masterpage.master", "toddler3", "toddler3") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler4 = new Template("test-toddler4-masterpage.master", "toddler4", "toddler4") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var baby2 = new Template("test-baby2-masterpage.master", "baby2", "baby2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var child2 = new Template("test-child2-masterpage.master", "child2", "child2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler3 = new Template("test-toddler3-masterpage.master", "toddler3", "toddler3") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var toddler4 = new Template("test-toddler4-masterpage.master", "toddler4", "toddler4") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
var baby2 = new Template("test-baby2-masterpage.master", "baby2", "baby2") { Content = @"<%@ Master Language=""C#"" %>" };
|
||||
|
||||
|
||||
child1.MasterTemplateAlias = parent.Alias;
|
||||
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
child2.MasterTemplateAlias = parent.Alias;
|
||||
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
child1.MasterTemplateAlias = parent.Alias;
|
||||
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
child2.MasterTemplateAlias = parent.Alias;
|
||||
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
|
||||
|
||||
toddler1.MasterTemplateAlias = child1.Alias;
|
||||
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
|
||||
toddler2.MasterTemplateAlias = child1.Alias;
|
||||
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
|
||||
toddler1.MasterTemplateAlias = child1.Alias;
|
||||
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
|
||||
toddler2.MasterTemplateAlias = child1.Alias;
|
||||
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
|
||||
|
||||
toddler3.MasterTemplateAlias = child2.Alias;
|
||||
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
|
||||
toddler4.MasterTemplateAlias = child2.Alias;
|
||||
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
|
||||
toddler3.MasterTemplateAlias = child2.Alias;
|
||||
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
|
||||
toddler4.MasterTemplateAlias = child2.Alias;
|
||||
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
|
||||
|
||||
baby1.MasterTemplateAlias = toddler2.Alias;
|
||||
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
|
||||
baby1.MasterTemplateAlias = toddler2.Alias;
|
||||
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
|
||||
|
||||
baby2.MasterTemplateAlias = toddler4.Alias;
|
||||
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
|
||||
baby2.MasterTemplateAlias = toddler4.Alias;
|
||||
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
|
||||
|
||||
repository.AddOrUpdate(parent);
|
||||
repository.AddOrUpdate(child1);
|
||||
repository.AddOrUpdate(child2);
|
||||
repository.AddOrUpdate(toddler1);
|
||||
repository.AddOrUpdate(toddler2);
|
||||
repository.AddOrUpdate(toddler3);
|
||||
repository.AddOrUpdate(toddler4);
|
||||
repository.AddOrUpdate(baby1);
|
||||
repository.AddOrUpdate(baby2);
|
||||
unitOfWork.Commit();
|
||||
repository.AddOrUpdate(parent);
|
||||
repository.AddOrUpdate(child1);
|
||||
repository.AddOrUpdate(child2);
|
||||
repository.AddOrUpdate(toddler1);
|
||||
repository.AddOrUpdate(toddler2);
|
||||
repository.AddOrUpdate(toddler3);
|
||||
repository.AddOrUpdate(toddler4);
|
||||
repository.AddOrUpdate(baby1);
|
||||
repository.AddOrUpdate(baby2);
|
||||
unitOfWork.Commit();
|
||||
|
||||
// Act
|
||||
var rootNode = repository.GetTemplateNode("parent");
|
||||
// Act
|
||||
var rootNode = repository.GetTemplateNode("parent");
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "parent"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child2"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler2"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler3"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler4"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2"));
|
||||
// Assert
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "parent"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child2"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler2"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler3"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler4"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1"));
|
||||
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//[Test]
|
||||
|
||||
@@ -231,6 +231,9 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public object Value { get; set; }
|
||||
public bool HasValue { get; set; }
|
||||
public object XPathValue { get; set; }
|
||||
|
||||
public string Alias { get { return PropertyTypeAlias; } }
|
||||
public Guid Version { get { return Guid.Empty; } }
|
||||
}
|
||||
|
||||
[PublishedContentModel("ContentType2")]
|
||||
|
||||
@@ -90,9 +90,9 @@ namespace Umbraco.Tests.Routing
|
||||
}
|
||||
|
||||
//test all template name styles to match the ActionName
|
||||
[TestCase("home-\\234^^*32page")]
|
||||
|
||||
//[TestCase("home-\\234^^*32page")] //TODO: This fails!
|
||||
[TestCase("home-page")]
|
||||
[TestCase("home-\\234^^*32page")]
|
||||
[TestCase("home-page")]
|
||||
[TestCase("home-page")]
|
||||
[TestCase("Home-Page")]
|
||||
|
||||
@@ -38,7 +38,83 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
//TODO Add test to verify there is only ONE newest document/content in cmsDocument table after updating.
|
||||
//TODO Add test to delete specific version (with and without deleting prior versions) and versions by date.
|
||||
|
||||
|
||||
[Test]
|
||||
public void Count_All()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(24, contentService.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Count_By_Content_Type()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(20, contentService.Count(contentTypeAlias: "umbBlah"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Count_Children()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
|
||||
contentTypeService.Save(contentType);
|
||||
var parent = contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
|
||||
|
||||
// Act
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
contentService.CreateContentWithIdentity("Test", parent, "umbBlah");
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(20, contentService.CountChildren(parent.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Count_Descendants()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
|
||||
contentTypeService.Save(contentType);
|
||||
var parent = contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
|
||||
|
||||
// Act
|
||||
IContent current = parent;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
current = contentService.CreateContentWithIdentity("Test", current, "umbBlah");
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(20, contentService.CountDescendants(parent.Id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Remove_Property_Type()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
@@ -8,10 +12,17 @@ namespace Umbraco.Tests.Services
|
||||
/// This is more of an integration test as it involves multiple layers
|
||||
/// as well as configuration.
|
||||
/// </summary>
|
||||
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerFixture)]
|
||||
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
|
||||
[TestFixture, RequiresSTA]
|
||||
public class LocalizationServiceTests : BaseServiceTest
|
||||
{
|
||||
private Guid _parentItemGuidId;
|
||||
private int _parentItemIntId;
|
||||
private Guid _childItemGuidId;
|
||||
private int _childItemIntId;
|
||||
private int _danishLangId;
|
||||
private int _englishLangId;
|
||||
|
||||
[SetUp]
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -23,5 +34,194 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Root_Dictionary_Items()
|
||||
{
|
||||
var rootItems = ServiceContext.LocalizationService.GetRootDictionaryItems();
|
||||
|
||||
Assert.NotNull(rootItems);
|
||||
Assert.IsTrue(rootItems.Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Determint_If_DictionaryItem_Exists()
|
||||
{
|
||||
var exists = ServiceContext.LocalizationService.DictionaryItemExists("Parent");
|
||||
Assert.IsTrue(exists);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_All_Languages()
|
||||
{
|
||||
var languages = ServiceContext.LocalizationService.GetAllLanguages();
|
||||
Assert.NotNull(languages);
|
||||
Assert.IsTrue(languages.Any());
|
||||
Assert.That(languages.Count(), Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Dictionary_Item_By_Int_Id()
|
||||
{
|
||||
var parentItem = ServiceContext.LocalizationService.GetDictionaryItemById(_parentItemIntId);
|
||||
Assert.NotNull(parentItem);
|
||||
|
||||
var childItem = ServiceContext.LocalizationService.GetDictionaryItemById(_childItemIntId);
|
||||
Assert.NotNull(childItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Dictionary_Item_By_Guid_Id()
|
||||
{
|
||||
var parentItem = ServiceContext.LocalizationService.GetDictionaryItemById(_parentItemGuidId);
|
||||
Assert.NotNull(parentItem);
|
||||
|
||||
var childItem = ServiceContext.LocalizationService.GetDictionaryItemById(_childItemGuidId);
|
||||
Assert.NotNull(childItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Dictionary_Item_By_Key()
|
||||
{
|
||||
var parentItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent");
|
||||
Assert.NotNull(parentItem);
|
||||
|
||||
var childItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child");
|
||||
Assert.NotNull(childItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Dictionary_Item_Children()
|
||||
{
|
||||
var item = ServiceContext.LocalizationService.GetDictionaryItemChildren(_parentItemGuidId);
|
||||
Assert.NotNull(item);
|
||||
Assert.That(item.Count(), Is.EqualTo(1));
|
||||
|
||||
foreach (var dictionaryItem in item)
|
||||
{
|
||||
Assert.IsFalse(string.IsNullOrEmpty(dictionaryItem.ItemKey));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Get_Language_By_Culture_Code()
|
||||
{
|
||||
var danish = ServiceContext.LocalizationService.GetLanguageByCultureCode("Danish");
|
||||
var english = ServiceContext.LocalizationService.GetLanguageByCultureCode("English");
|
||||
Assert.NotNull(danish);
|
||||
Assert.NotNull(english);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_GetLanguageById()
|
||||
{
|
||||
var danish = ServiceContext.LocalizationService.GetLanguageById(_danishLangId);
|
||||
var english = ServiceContext.LocalizationService.GetLanguageById(_englishLangId);
|
||||
Assert.NotNull(danish);
|
||||
Assert.NotNull(english);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_GetLanguageByIsoCode()
|
||||
{
|
||||
var danish = ServiceContext.LocalizationService.GetLanguageByIsoCode("da-DK");
|
||||
var english = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-GB");
|
||||
Assert.NotNull(danish);
|
||||
Assert.NotNull(english);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Does_Not_Fail_When_Language_Doesnt_Exist()
|
||||
{
|
||||
var language = ServiceContext.LocalizationService.GetLanguageByIsoCode("sv-SE");
|
||||
Assert.Null(language);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Does_Not_Fail_When_DictionaryItem_Doesnt_Exist()
|
||||
{
|
||||
var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("RandomKey");
|
||||
Assert.Null(item);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Delete_Language()
|
||||
{
|
||||
var norwegian = new Language("nb-NO") { CultureName = "Norwegian" };
|
||||
ServiceContext.LocalizationService.Save(norwegian, 0);
|
||||
Assert.That(norwegian.HasIdentity, Is.True);
|
||||
var languageId = norwegian.Id;
|
||||
|
||||
ServiceContext.LocalizationService.Delete(norwegian);
|
||||
|
||||
var language = ServiceContext.LocalizationService.GetLanguageById(languageId);
|
||||
Assert.Null(language);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Delete_DictionaryItem()
|
||||
{
|
||||
var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child");
|
||||
Assert.NotNull(item);
|
||||
|
||||
ServiceContext.LocalizationService.Delete(item);
|
||||
|
||||
var deletedItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child");
|
||||
Assert.Null(deletedItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizationService_Can_Update_Existing_DictionaryItem()
|
||||
{
|
||||
var item = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child");
|
||||
foreach (var translation in item.Translations)
|
||||
{
|
||||
translation.Value = translation.Value + "UPDATED";
|
||||
}
|
||||
|
||||
ServiceContext.LocalizationService.Save(item);
|
||||
|
||||
var updatedItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Child");
|
||||
Assert.NotNull(updatedItem);
|
||||
|
||||
foreach (var translation in updatedItem.Translations)
|
||||
{
|
||||
Assert.That(translation.Value.EndsWith("UPDATED"), Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
public override void CreateTestData()
|
||||
{
|
||||
var danish = new Language("da-DK") { CultureName = "Danish" };
|
||||
var english = new Language("en-GB") { CultureName = "English" };
|
||||
ServiceContext.LocalizationService.Save(danish, 0);
|
||||
ServiceContext.LocalizationService.Save(english, 0);
|
||||
_danishLangId = danish.Id;
|
||||
_englishLangId = english.Id;
|
||||
|
||||
var parentItem = new DictionaryItem("Parent")
|
||||
{
|
||||
Translations = new List<IDictionaryTranslation>
|
||||
{
|
||||
new DictionaryTranslation(english, "ParentValue"),
|
||||
new DictionaryTranslation(danish, "ForældreVærdi")
|
||||
}
|
||||
};
|
||||
ServiceContext.LocalizationService.Save(parentItem);
|
||||
_parentItemGuidId = parentItem.Key;
|
||||
_parentItemIntId = parentItem.Id;
|
||||
|
||||
var childItem = new DictionaryItem(parentItem.Key, "Child")
|
||||
{
|
||||
Translations = new List<IDictionaryTranslation>
|
||||
{
|
||||
new DictionaryTranslation(english, "ChildValue"),
|
||||
new DictionaryTranslation(danish, "BørnVærdi")
|
||||
}
|
||||
};
|
||||
ServiceContext.LocalizationService.Save(childItem);
|
||||
_childItemGuidId = childItem.Key;
|
||||
_childItemIntId = childItem.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
@@ -352,6 +353,34 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsNull(ServiceContext.MemberService.GetByEmail("do@not.find"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Member_Name()
|
||||
{
|
||||
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
|
||||
ServiceContext.MemberTypeService.Save(memberType);
|
||||
IMember member = MockedMember.CreateSimpleMember(memberType, "Test Real Name", "test@test.com", "pass", "testUsername");
|
||||
ServiceContext.MemberService.Save(member);
|
||||
|
||||
|
||||
Assert.AreEqual("Test Real Name", member.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Member_Name_In_Created_Event()
|
||||
{
|
||||
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
|
||||
ServiceContext.MemberTypeService.Save(memberType);
|
||||
|
||||
TypedEventHandler<IMemberService, NewEventArgs<IMember>> callback = (sender, args) =>
|
||||
{
|
||||
Assert.AreEqual("Test Real Name", args.Entity.Name);
|
||||
};
|
||||
|
||||
MemberService.Created += callback;
|
||||
var member = ServiceContext.MemberService.CreateMember("testUsername", "test@test.com", "Test Real Name", memberType);
|
||||
MemberService.Created -= callback;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_By_Username()
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Sync
|
||||
[Test]
|
||||
public void RefreshIntId()
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
for (var i = 1; i < 11; i++)
|
||||
{
|
||||
DistributedCache.Instance.Refresh(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Tests.Sync
|
||||
[Test]
|
||||
public void RemoveIds()
|
||||
{
|
||||
for (var i = 0; i < 12; i++)
|
||||
for (var i = 1; i < 13; i++)
|
||||
{
|
||||
DistributedCache.Instance.Remove(Guid.Parse("E0F452CB-DCB2-4E84-B5A5-4F01744C5C73"), i);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Web;
|
||||
using Moq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
//NOTE: This is just a POC! Looking at the simplest way to expose some code so people can very easily test
|
||||
// their Umbraco controllers, etc....
|
||||
public class DisposableUmbracoTest : DisposableObject
|
||||
{
|
||||
public ApplicationContext ApplicationContext { get; set; }
|
||||
public UmbracoContext UmbracoContext { get; set; }
|
||||
|
||||
public DisposableUmbracoTest(ApplicationContext applicationContext)
|
||||
{
|
||||
//init umb context
|
||||
var umbctx = UmbracoContext.EnsureContext(
|
||||
new Mock<HttpContextBase>().Object,
|
||||
applicationContext,
|
||||
true);
|
||||
|
||||
Init(applicationContext, umbctx);
|
||||
}
|
||||
|
||||
public DisposableUmbracoTest(ApplicationContext applicationContext, UmbracoContext umbracoContext)
|
||||
{
|
||||
Init(applicationContext, umbracoContext);
|
||||
}
|
||||
|
||||
private void Init(ApplicationContext applicationContext, UmbracoContext umbracoContext)
|
||||
{
|
||||
TestHelper.SetupLog4NetForTests();
|
||||
|
||||
ApplicationContext = applicationContext;
|
||||
UmbracoContext = umbracoContext;
|
||||
|
||||
ApplicationContext.Current = applicationContext;
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
|
||||
Resolution.Freeze();
|
||||
}
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
ApplicationContext.Current = null;
|
||||
UmbracoContext.Current = null;
|
||||
Resolution.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
@@ -94,7 +95,9 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
var httpContextMock = new Mock<HttpContextBase>();
|
||||
httpContextMock.Setup(x => x.Cache).Returns(HttpRuntime.Cache);
|
||||
httpContextMock.Setup(x => x.Items).Returns(new Dictionary<object, object>());
|
||||
//note: foreach on Items should return DictionaryEntries!
|
||||
//httpContextMock.Setup(x => x.Items).Returns(new Dictionary<object, object>());
|
||||
httpContextMock.Setup(x => x.Items).Returns(new Hashtable());
|
||||
httpContextMock.Setup(x => x.Request).Returns(requestMock.Object);
|
||||
httpContextMock.Setup(x => x.Server).Returns(serverMock.Object);
|
||||
httpContextMock.Setup(x => x.Response).Returns(responseMock.Object);
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Tests.PartialTrust;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Cache;
|
||||
using Umbraco.Web.Scheduling;
|
||||
using UmbracoExamine;
|
||||
using umbraco;
|
||||
using umbraco.presentation;
|
||||
@@ -67,7 +68,7 @@ namespace Umbraco.Tests
|
||||
Assert.AreEqual(typeof(UmbracoEventManager), t5.Result);
|
||||
|
||||
var t6 = TypeHelper.GetLowestBaseType(typeof (IApplicationEventHandler),
|
||||
typeof (LegacyScheduledTasks),
|
||||
typeof (Scheduler),
|
||||
typeof(CacheRefresherEventHandler));
|
||||
Assert.IsTrue(t6.Success);
|
||||
Assert.AreEqual(typeof(IApplicationEventHandler), t6.Result);
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Examine">
|
||||
<Reference Include="Examine, Version=0.1.57.2941, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Examine.0.1.55.2941\lib\Examine.dll</HintPath>
|
||||
<HintPath>..\packages\Examine.0.1.57.2941\lib\Examine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@@ -122,17 +122,16 @@
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Razor">
|
||||
<HintPath>..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll</HintPath>
|
||||
<Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages">
|
||||
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages.Deployment">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
|
||||
<Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
@@ -173,6 +172,7 @@
|
||||
<Compile Include="Models\LanguageTests.cs" />
|
||||
<Compile Include="Models\MemberGroupTests.cs" />
|
||||
<Compile Include="Models\MemberTests.cs" />
|
||||
<Compile Include="Models\PreValueCollectionTests.cs" />
|
||||
<Compile Include="Models\PropertyGroupTests.cs" />
|
||||
<Compile Include="Models\PropertyTypeTests.cs" />
|
||||
<Compile Include="Models\RelationTests.cs" />
|
||||
@@ -183,6 +183,7 @@
|
||||
<Compile Include="Models\UmbracoEntityTests.cs" />
|
||||
<Compile Include="Models\UserTests.cs" />
|
||||
<Compile Include="Models\UserTypeTests.cs" />
|
||||
<Compile Include="Mvc\SurfaceControllerTests.cs" />
|
||||
<Compile Include="Mvc\UmbracoViewPageTests.cs" />
|
||||
<Compile Include="Persistence\Auditing\AuditTests.cs" />
|
||||
<Compile Include="BootManagers\CoreBootManagerTests.cs" />
|
||||
@@ -416,6 +417,7 @@
|
||||
<Compile Include="TestHelpers\Entities\MockedContentTypes.cs" />
|
||||
<Compile Include="TestHelpers\Entities\MockedEntity.cs" />
|
||||
<Compile Include="TestHelpers\Entities\MockedMedia.cs" />
|
||||
<Compile Include="TestHelpers\DisposableUmbracoTest.cs" />
|
||||
<Compile Include="Trees\BaseContentTreeTests.cs" />
|
||||
<Compile Include="Trees\BaseMediaTreeTests.cs" />
|
||||
<Compile Include="UmbracoExamine\ContentServiceTest.cs" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Examine" version="0.1.55.2941" targetFramework="net40" />
|
||||
<package id="Examine" version="0.1.57.2941" targetFramework="net40" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
|
||||
|
||||
@@ -109,9 +109,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Examine">
|
||||
<Reference Include="Examine, Version=0.1.57.2941, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Examine.0.1.55.2941\lib\Examine.dll</HintPath>
|
||||
<HintPath>..\packages\Examine.0.1.57.2941\lib\Examine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@@ -158,12 +159,14 @@
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.EnterpriseServices" />
|
||||
@@ -1608,7 +1611,6 @@
|
||||
<Content Include="Umbraco\Dashboard\Images\starterkit32x32.png" />
|
||||
<Content Include="Umbraco\Dashboard\Images\TV.png" />
|
||||
<Content Include="Umbraco\Dashboard\Images\ZipFile.png" />
|
||||
<Content Include="Umbraco\Dashboard\Swfs\AIRInstallBadge.swf" />
|
||||
<Content Include="Umbraco\Dashboard\Swfs\expressinstall.swf" />
|
||||
<Content Include="Umbraco\Images\Editor\insMacroSB.png" />
|
||||
<Content Include="Umbraco\Images\Editor\insRazorMacro.png" />
|
||||
@@ -2100,7 +2102,6 @@
|
||||
<Content Include="Install\Steps\welcome.ascx">
|
||||
<SubType>UserControl</SubType>
|
||||
</Content>
|
||||
<Content Include="Umbraco\cacheBrowser.aspx" />
|
||||
<Content Include="Umbraco\Config\Create\UI.xml" />
|
||||
<Content Include="Umbraco\Config\Lang\en.xml">
|
||||
<SubType>Designer</SubType>
|
||||
@@ -2674,9 +2675,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>6200</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>6220</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:6200</IISUrl>
|
||||
<IISUrl>http://localhost:6220</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -223,16 +223,47 @@
|
||||
<!-- <task log="true" alias="test60" interval="60" url="http://localhost/umbraco/test.aspx"/>-->
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls make umbraco use webservices to handle cache refreshing -->
|
||||
<!-- distributed calls must be enabled when using Umbraco in a load balanced environment -->
|
||||
<distributedCall enable="false">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
|
||||
<!--
|
||||
When distributed call is enabled, you need to add all of the servers part taking in load balancing
|
||||
to the server list below.
|
||||
-->
|
||||
|
||||
<servers>
|
||||
<!-- add ip number or hostname, make sure that it can be reached from all servers -->
|
||||
<!-- you can also add optional attributes to force a protocol or port number (see #2) -->
|
||||
<!-- <server>127.0.0.1</server>-->
|
||||
<!-- <server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>-->
|
||||
|
||||
<!--
|
||||
Add ip number or hostname, make sure that it can be reached from all servers
|
||||
you can also add optional attributes to force a protocol or port number.
|
||||
|
||||
Examples:
|
||||
|
||||
<server>127.0.0.1</server>
|
||||
<server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>
|
||||
|
||||
Generally when setting up load balancing you will designate a 'master' server,
|
||||
Umbraco will always assume that the FIRST server listed in this list is the 'master'.
|
||||
(NOTE: Not all load balancing scenarios have a 'master', depends on how you are setting it up)
|
||||
|
||||
In order for scheduled tasks (including scheduled publishing) to work properly when load balancing, each
|
||||
server in the load balanced environment needs to know if it is the 'master'. In order for servers
|
||||
to know this or not, they need to compare some values against the servers listed. These values
|
||||
are either: serverName or appId. You should not enter both values but appId will always supersede serverName.
|
||||
The serverName is the easiest and will work so long as you are not load balancing your site on the same server.
|
||||
If you are doing this, then you will need to use appId which is equivalent to the value returned from
|
||||
HttpRuntime.AppDomainAppId. It is recommended that you set either the serverName or appId for all servers
|
||||
registered here if possible, not just the first one.
|
||||
|
||||
Examples:
|
||||
|
||||
<server serverName="MyServer">server1.mysite.com</server>
|
||||
<server appId="/LM/W3SVC/69/ROOT">server1.mysite.com</server>
|
||||
-->
|
||||
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
|
||||
@@ -223,16 +223,48 @@
|
||||
<!-- <task log="true" alias="test60" interval="60" url="http://localhost/umbraco/test.aspx"/>-->
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls make umbraco use webservices to handle cache refreshing -->
|
||||
<!-- distributed calls must be enabled when using Umbraco in a load balanced environment -->
|
||||
<distributedCall enable="false">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
|
||||
<!--
|
||||
When distributed call is enabled, you need to add all of the servers part taking in load balancing
|
||||
to the server list below.
|
||||
-->
|
||||
|
||||
<servers>
|
||||
<!-- add ip number or hostname, make sure that it can be reached from all servers -->
|
||||
<!-- you can also add optional attributes to force a protocol or port number (see #2) -->
|
||||
<!-- <server>127.0.0.1</server>-->
|
||||
<!-- <server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>-->
|
||||
|
||||
<!--
|
||||
Add ip number or hostname, make sure that it can be reached from all servers
|
||||
you can also add optional attributes to force a protocol or port number.
|
||||
|
||||
Examples:
|
||||
|
||||
<server>127.0.0.1</server>
|
||||
<server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>
|
||||
|
||||
Generally when setting up load balancing you will designate a 'master' server,
|
||||
Umbraco will always assume that the FIRST server listed in this list is the 'master'.
|
||||
(NOTE: Not all load balancing scenarios have a 'master', depends on how you are setting it up)
|
||||
|
||||
In order for scheduled tasks (including scheduled publishing) to work properly when load balancing, each
|
||||
server in the load balanced environment needs to know if it is the 'master'. In order for servers
|
||||
to know this or not, they need to compare some values against the servers listed. These values
|
||||
are either: serverName or appId. You should not enter both values but appId will always supersede serverName.
|
||||
The serverName is the easiest and will work so long as you are not load balancing your site on the same server.
|
||||
If you are doing this, then you will need to use appId which is equivalent to the value returned from
|
||||
HttpRuntime.AppDomainAppId. It is recommended that you set either the serverName or appId for all servers
|
||||
registered here if possible, not just the first one.
|
||||
|
||||
Examples:
|
||||
|
||||
<server serverName="MyServer">server1.mysite.com</server>
|
||||
<server appId="/LM/W3SVC/69/ROOT">server1.mysite.com</server>
|
||||
-->
|
||||
|
||||
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<packages>
|
||||
<package id="ClientDependency" version="1.7.1.2" targetFramework="net40" />
|
||||
<package id="ClientDependency-Mvc" version="1.7.0.4" targetFramework="net40" />
|
||||
<package id="Examine" version="0.1.55.2941" targetFramework="net40" />
|
||||
<package id="Examine" version="0.1.57.2941" targetFramework="net40" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
|
||||
@@ -13,7 +13,7 @@
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net40" requireReinstallation="True" />
|
||||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Web.Helpers" version="1.0.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
|
||||
<package id="MiniProfiler" version="2.1.0" targetFramework="net40" />
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<%@ Page language="c#" Codebehind="cacheBrowser.aspx.cs" AutoEventWireup="True" Inherits="umbraco.cms.presentation.cacheBrowser" trace="true" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<title>cacheBrowser</title>
|
||||
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
|
||||
<meta content="C#" name="CODE_LANGUAGE">
|
||||
<meta content="JavaScript" name="vs_defaultClientScript">
|
||||
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
|
||||
</HEAD>
|
||||
<body>
|
||||
<form id="Form1" method="post" runat="server">
|
||||
<asp:Button id="Button1" runat="server" Text="Button" onclick="Button1_Click"></asp:Button>
|
||||
</form>
|
||||
</body>
|
||||
</HTML>
|
||||
@@ -1,50 +1,25 @@
|
||||
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="DesktopMediaUploader.ascx.cs" Inherits="umbraco.presentation.umbraco.dashboard.DesktopMediaUploader" %>
|
||||
<%@ Register Assembly="controls" Namespace="umbraco.uicontrols" TagPrefix="umb" %>
|
||||
<%@ Register Assembly="ClientDependency.Core" Namespace="ClientDependency.Core.Controls" TagPrefix="umb" %>
|
||||
|
||||
<umb:CssInclude runat="server" FilePath="propertypane/style.css" PathNameAlias="UmbracoClient" />
|
||||
<umb:JsInclude runat="server" FilePath="dashboard/scripts/swfobject.js" PathNameAlias="UmbracoRoot" />
|
||||
|
||||
<div class="dashboardWrapper">
|
||||
<h2>Desktop Media Uploader</h2>
|
||||
<img src="./dashboard/images/dmu.png" alt="Umbraco" class="dashboardIcon" />
|
||||
<p><strong>Desktop Media Uploader</strong> is a small desktop application that you can install on your computer which allows you to easily upload media items directly to the media section.</p>
|
||||
<p>The badge below will auto configure itself based upon whether you already have <strong>Desktop Media Uploader</strong> installed or not.</p>
|
||||
<p>Just click the <strong>Install Now / Upgrade Now / Launch Now</strong> link to perform that action.</p>
|
||||
<div class="dashboardColWrapper">
|
||||
<div class="dashboardCols">
|
||||
<div class="dashboardCol">
|
||||
<asp:Panel ID="Panel1" runat="server"></asp:Panel>
|
||||
<asp:Panel ID="Panel2" runat="server">
|
||||
<p>
|
||||
<div id="dmu-badge">
|
||||
Download <a href="<%= FullyQualifiedAppPath %>umbraco/dashboard/air/desktopmediauploader.air">Desktop Media Uploader</a> now.<br /><br /><span id="Span1">This application requires Adobe® AIR™ to be installed for <a href="http://airdownload.adobe.com/air/mac/download/latest/AdobeAIR.dmg">Mac OS</a> or <a href="http://airdownload.adobe.com/air/win/download/latest/AdobeAIRInstaller.exe">Windows</a>.
|
||||
</div>
|
||||
</p>
|
||||
<script type="text/javascript">
|
||||
// <![CDATA[
|
||||
var flashvars = {
|
||||
appid: "org.umbraco.DesktopMediaUploader",
|
||||
appname: "Desktop Media Uploader",
|
||||
appversion: "v2.1.0",
|
||||
appurl: "<%= FullyQualifiedAppPath %>umbraco/dashboard/air/desktopmediauploader.air",
|
||||
applauncharg: "<%= AppLaunchArg %>",
|
||||
image: "/umbraco/dashboard/images/dmu-badge.jpg?2.1.0",
|
||||
airversion: "2.0"
|
||||
};
|
||||
var params = {
|
||||
menu: "false",
|
||||
wmode: "opaque"
|
||||
};
|
||||
var attributes = {
|
||||
style: "margin-bottom:10px;"
|
||||
};
|
||||
|
||||
swfobject.embedSWF("/umbraco/dashboard/swfs/airinstallbadge.swf", "dmu-badge", "215", "180", "9.0.115", "/umbraco/dashboard/swfs/expressinstall.swf", flashvars, params, attributes);
|
||||
// ]]>
|
||||
</script>
|
||||
<div id="dmu-badge">
|
||||
<p>
|
||||
<span id="Span1">This application requires Adobe® AIR™ to be installed for <a href="http://airdownload.adobe.com/air/mac/download/latest/AdobeAIR.dmg">Mac OS</a> or <a href="http://airdownload.adobe.com/air/win/download/latest/AdobeAIRInstaller.exe">Windows</a></span>.<br />
|
||||
After Air is installed you can install the <a href="<%= FullyQualifiedAppPath %>umbraco/Dashboard/air/DesktopMediaUploader.air">Desktop Media Uploader by clicking here</a>.
|
||||
</p>
|
||||
</div>
|
||||
</asp:Panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
@@ -5,9 +5,57 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
top.right.document.location = window.location.search.substring(1);
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
//This is a genius way of parsing a uri
|
||||
//https://gist.github.com/jlong/2428561
|
||||
|
||||
try {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = window.location.search.substring(1);
|
||||
|
||||
// => "http:"
|
||||
if (!parser.protocol || (parser.protocol.toLowerCase() != "http:" && parser.protocol.toLowerCase() != "https:")) {
|
||||
throw "invalid protocol";
|
||||
};
|
||||
|
||||
// => "example.com"
|
||||
if (!parser.hostname || parser.hostname == "") {
|
||||
throw "invalid hostname";
|
||||
}
|
||||
|
||||
//parser.port; // => "3000"
|
||||
|
||||
// => "/pathname/"
|
||||
if (!parser.pathname || ((parser.pathname.length - parser.pathname.indexOf("/developer/packages/installer.aspx")) != "/developer/packages/installer.aspx".length))
|
||||
{
|
||||
throw "invalid pathname";
|
||||
}
|
||||
|
||||
// => "?search=test"
|
||||
if (!parser.search || parser.search.indexOf("?repoGuid") != 0) {
|
||||
throw "invalid search";
|
||||
}
|
||||
|
||||
// => "#hash"
|
||||
if (parser.hash && parser.hash != "") {
|
||||
throw "invalid hash";
|
||||
}
|
||||
|
||||
//parser.host; // => "example.com:3000"
|
||||
|
||||
if (!top.right) {
|
||||
throw "invalid document";
|
||||
}
|
||||
|
||||
top.right.document.location = window.location.search.substring(1);
|
||||
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user