Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd2fe1939a | |||
| 81e56bed9e | |||
| 52457fea21 | |||
| 07ec9aa813 | |||
| ba89aec34f | |||
| 29533820ab | |||
| 4366500fad | |||
| ee6e1a9d8b | |||
| 3b95184f04 | |||
| 9da7e108da | |||
| cb491d45ab | |||
| 344b880ba7 | |||
| c958f12bc1 | |||
| 9a973d0ccc | |||
| b170053e87 | |||
| db4509de2b | |||
| 5c91b822e3 | |||
| e6ddf63e56 | |||
| 84ac029372 | |||
| 5d59741612 | |||
| 2ffd0413e8 | |||
| dc4e6ee01c | |||
| 1d8e217c6f | |||
| 0b3136cf20 | |||
| b0e96b256a | |||
| 72a966b0fa | |||
| bf222f26cd | |||
| be9f4b70e2 | |||
| 0060f0019e | |||
| 632cd221d9 | |||
| 46d5215d9e | |||
| aa43656cb9 | |||
| e4837d1962 | |||
| 40868d7cd2 | |||
| 0fde3f7b5d | |||
| 76f0eac5f8 | |||
| 2352d258b9 | |||
| 2dc38a29cf | |||
| 1aa162e255 | |||
| 342999a678 | |||
| 5cb210c7b6 | |||
| b80ab20cb4 | |||
| bac6e71e7f | |||
| 9a36d74418 | |||
| 39eaa4a15e | |||
| 2fdf040f73 | |||
| aec956dd2e | |||
| 65bb1e1e20 | |||
| 3dabd73ec2 | |||
| 90e5440518 | |||
| 7cfd2d6593 | |||
| 6382515258 | |||
| fd2b9ce67d | |||
| 1284417a58 | |||
| b57642003c | |||
| 9ee59e8e38 | |||
| a5f81ae6c2 | |||
| 08cc608dc8 | |||
| 71839d53a6 | |||
| 7508613d13 | |||
| 9626e91c83 | |||
| 83b6375f04 | |||
| 52cd44a700 | |||
| 0c8ff187aa | |||
| 3af74bd4fd | |||
| c9e2e1e116 | |||
| c27f73e54a | |||
| cede13d180 | |||
| 3ffb2772b6 | |||
| e6f0d4e056 | |||
| 23c7eafaf5 | |||
| 90892ce818 | |||
| e62d1559b1 | |||
| 81934c47d2 | |||
| 80379e116d | |||
| 89081b86af | |||
| 216e2255f2 | |||
| 9db3dbd730 | |||
| 8a1dd96294 | |||
| 67bebd0d2d | |||
| 4f34fbd2c8 | |||
| 8cda6e4062 | |||
| d8209efefa | |||
| 00180d2120 | |||
| 66e768afee | |||
| bba109cb28 | |||
| 39ede8c47a | |||
| fec84b8ce3 | |||
| a00e5d54aa | |||
| 331813e370 | |||
| 6c36f43276 | |||
| e8f4e7663f | |||
| 9f116c00d5 | |||
| 7e61b25746 | |||
| 482750ab6a | |||
| 40d1099fbd | |||
| 2d050a0c88 | |||
| 103f4f33bd | |||
| 727f9355a0 | |||
| e269a0b9bb | |||
| 861b1cefe5 | |||
| 23301817bf | |||
| f5842c82a6 | |||
| 4baf3ed99a | |||
| 3c09cd7674 |
@@ -78,6 +78,12 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
|
||||
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.panel.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.palettes.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.loader.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.front.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.config.js
|
||||
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
|
||||
|
||||
+1
-3
@@ -4,7 +4,7 @@ IF NOT EXIST UmbracoVersion.txt (
|
||||
GOTO :showerror
|
||||
)
|
||||
SET /p release=<UmbracoVersion.txt
|
||||
SET comment=
|
||||
SET comment=alpha
|
||||
SET version=%release%
|
||||
|
||||
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
|
||||
@@ -14,14 +14,12 @@ ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %rel
|
||||
|
||||
ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice
|
||||
SET nuGetFolder=%CD%\..\src\packages\
|
||||
..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/
|
||||
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
|
||||
|
||||
ECHO Removing the belle build folder to make sure everything is clean as a whistle
|
||||
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
|
||||
|
||||
ECHO Removing existing built files to make sure everything is clean as a whistle
|
||||
RMDIR /Q /S _BuildOutput
|
||||
DEL /F /Q UmbracoCms.*.zip
|
||||
DEL /F /Q UmbracoCms.*.nupkg
|
||||
DEL /F /Q webpihash.txt
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.30506.0,5.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.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.1" />
|
||||
<dependency id="Microsoft.Net.Http" version="[2.0.20710.0, 3.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="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)" />
|
||||
@@ -31,10 +29,9 @@
|
||||
<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="AutoMapper" version="[3.0.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.4, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.57, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[1.9.5, 2.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[3.3.0, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.2, 7.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[1.9.0, 2.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[3.2.3, 4.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -44,6 +41,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.1.6
|
||||
7.2.0
|
||||
Binary file not shown.
@@ -77,7 +77,6 @@
|
||||
<Compile Include="SqlCEUtility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,204 +1,218 @@
|
||||
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
|
||||
{
|
||||
// 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
|
||||
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 (WriteLock)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
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)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
using (WriteLock)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
RemoveEntry(cacheKey);
|
||||
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 (WriteLock)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
// 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);
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
using (WriteLock)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
// 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;
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT;
|
||||
})
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x =>
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
// 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);
|
||||
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 plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
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)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
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 plen = CacheItemPrefix.Length + 1;
|
||||
using (WriteLock)
|
||||
var keysToRemove = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
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 virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
using (ReadLock)
|
||||
var found = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
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 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
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
var result = DictionaryCache.Get(GetCacheKey(cacheKey));
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
#endregion
|
||||
/// <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>
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
@@ -11,115 +12,36 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
{
|
||||
// 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;
|
||||
private readonly Func<HttpContextBase> _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
|
||||
public HttpRequestCacheProvider(HttpContext context)
|
||||
{
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
_context = () => new HttpContextWrapper(context);
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
public HttpRequestCacheProvider(Func<HttpContextBase> context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
{
|
||||
//_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
|
||||
{
|
||||
return new MonitorLock(ContextItems.SyncRoot);
|
||||
var ctx = _context();
|
||||
return new DictionaryCacheWrapper(
|
||||
ctx.Items,
|
||||
o => ctx.Items[o],
|
||||
o => ctx.Items.Remove(o));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> 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;
|
||||
var ctx = _context();
|
||||
var ck = GetCacheKey(cacheKey);
|
||||
return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
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
|
||||
@@ -13,52 +14,48 @@ 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 IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
return _cache.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
|
||||
get { return _wrapper; }
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
{
|
||||
_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>
|
||||
/// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
@@ -84,64 +81,25 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
// 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))
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
{
|
||||
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
|
||||
var result = DictionaryCache.Get(cacheKey);
|
||||
if (result == 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
@@ -154,10 +112,6 @@ 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>
|
||||
@@ -170,22 +124,15 @@ 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)
|
||||
{
|
||||
// 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)
|
||||
var result = getCacheItem();
|
||||
if (result == null) return;
|
||||
|
||||
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);
|
||||
|
||||
using (new WriteLock(_locker))
|
||||
{
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
_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)
|
||||
@@ -197,7 +144,5 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -16,37 +16,17 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private static 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");
|
||||
@@ -55,7 +35,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);
|
||||
@@ -64,179 +44,131 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof (T);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
using (new WriteLock(_locker))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
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();
|
||||
return (from c in MemoryCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
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();
|
||||
return (from c in MemoryCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
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
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
public virtual 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)
|
||||
{
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
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
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
result = new Lazy<object>(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
object result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
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 HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider())
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,5 @@
|
||||
string ForcePortnumber { get; }
|
||||
string ForceProtocol { get; }
|
||||
string ServerAddress { get; }
|
||||
|
||||
string AppId { get; }
|
||||
string ServerName { get; }
|
||||
}
|
||||
}
|
||||
@@ -26,24 +26,5 @@
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
|
||||
public string AppId
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("appId") == null
|
||||
? null
|
||||
: RawXml.Attribute("appId").Value;
|
||||
}
|
||||
}
|
||||
public string ServerName
|
||||
{
|
||||
get
|
||||
{
|
||||
return RawXml.Attribute("serverName") == null
|
||||
? null
|
||||
: RawXml.Attribute("serverName").Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.1.6");
|
||||
private static readonly Version Version = new Version("7.2.0");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
public static string CurrentComment { get { return "alpha"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -70,6 +70,11 @@
|
||||
|
||||
//TODO: Fill in the rest!
|
||||
|
||||
/// <summary>
|
||||
/// alias for the media tree.
|
||||
/// </summary>
|
||||
public const string Templates = "template";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,22 +279,6 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string AltTemplate = "altTemplate";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the alias identifiers for Umbraco relation types.
|
||||
/// </summary>
|
||||
public static class RelationTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyName = "Relate Document On Copy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,51 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the constants used for Umbraco packages in the package.config xml
|
||||
/// </summary>
|
||||
public static class Packaging
|
||||
{
|
||||
public const string UmbPackageNodeName = "umbPackage";
|
||||
public const string DataTypesNodeName = "DataTypes";
|
||||
public const string PackageXmlFileName = "package.xml";
|
||||
public const string UmbracoPackageExtention = ".umb";
|
||||
public const string DataTypeNodeName = "DataType";
|
||||
public const string LanguagesNodeName = "Languages";
|
||||
public const string FilesNodeName = "files";
|
||||
public const string StylesheetsNodeName = "Stylesheets";
|
||||
public const string TemplatesNodeName = "Templates";
|
||||
public const string NameNodeName = "Name";
|
||||
public const string TemplateNodeName = "Template";
|
||||
public const string AliasNodeNameSmall = "alias";
|
||||
public const string AliasNodeNameCapital = "Alias";
|
||||
public const string DictionaryItemsNodeName = "DictionaryItems";
|
||||
public const string DictionaryItemNodeName = "DictionaryItem";
|
||||
public const string MacrosNodeName = "Macros";
|
||||
public const string DocumentsNodeName = "Documents";
|
||||
public const string DocumentSetNodeName = "DocumentSet";
|
||||
public const string DocumentTypesNodeName = "DocumentTypes";
|
||||
public const string DocumentTypeNodeName = "DocumentType";
|
||||
public const string FileNodeName = "file";
|
||||
public const string OrgNameNodeName = "orgName";
|
||||
public const string OrgPathNodeName = "orgPath";
|
||||
public const string GuidNodeName = "guid";
|
||||
public const string StylesheetNodeName = "styleSheet";
|
||||
public const string MacroNodeName = "macro";
|
||||
public const string InfoNodeName = "info";
|
||||
public const string PackageRequirementsMajorXpath = "./package/requirements/major";
|
||||
public const string PackageRequirementsMinorXpath = "./package/requirements/minor";
|
||||
public const string PackageRequirementsPatchXpath = "./package/requirements/patch";
|
||||
public const string PackageNameXpath = "./package/name";
|
||||
public const string PackageVersionXpath = "./package/version";
|
||||
public const string PackageUrlXpath = "./package/url";
|
||||
public const string PackageLicenseXpath = "./package/license";
|
||||
public const string PackageLicenseXpathUrlAttribute = "url";
|
||||
public const string AuthorNameXpath = "./author/name";
|
||||
public const string AuthorWebsiteXpath = "./author/website";
|
||||
public const string ReadmeXpath = "./readme";
|
||||
public const string ControlNodeName = "control";
|
||||
public const string ActionNodeName = "Action";
|
||||
public const string ActionsNodeName = "Actions";
|
||||
public const string UndoNodeAttribute = "undo";
|
||||
public const string RunatNodeAttribute = "runat";
|
||||
}
|
||||
}
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the constants used for Umbraco packages in the package.config xml
|
||||
/// </summary>
|
||||
public static class Packaging
|
||||
{
|
||||
public const string UmbPackageNodeName = "umbPackage";
|
||||
public const string DataTypesNodeName = "DataTypes";
|
||||
public const string PackageXmlFileName = "package.xml";
|
||||
public const string UmbracoPackageExtention = ".umb";
|
||||
public const string DataTypeNodeName = "DataType";
|
||||
public const string LanguagesNodeName = "Languages";
|
||||
public const string FilesNodeName = "Files";
|
||||
public const string StylesheetsNodeName = "Stylesheets";
|
||||
public const string TemplatesNodeName = "Templates";
|
||||
public const string OrgnameNodeName = "orgName";
|
||||
public const string NameNodeName = "Name";
|
||||
public const string TemplateNodeName = "Template";
|
||||
public const string AliasNodeName = "Alias";
|
||||
public const string DictionaryitemsNodeName = "DictionaryItems";
|
||||
public const string MacrosNodeName = "macros";
|
||||
public const string DocumentSetNodeName = "DocumentSet";
|
||||
public const string DocumentTypesNodeName = "DocumentTypes";
|
||||
public const string DocumentTypeNodeName = "DocumentType";
|
||||
public const string FileNodeName = "file";
|
||||
public const string OrgPathNodeName = "orgPath";
|
||||
public const string GuidNodeName = "guid";
|
||||
public const string StylesheetNodeName = "styleSheet";
|
||||
public const string MacroNodeName = "macro";
|
||||
public const string InfoNodeName = "info";
|
||||
public const string PackageRequirementsMajorXpath = "/package/requirements/major";
|
||||
public const string PackageRequirementsMinorXpath = "/package/requirements/minor";
|
||||
public const string PackageRequirementsPatchXpath = "/package/requirements/patch";
|
||||
public const string PackageNameXpath = "/package/name";
|
||||
public const string PackageVersionXpath = "/package/version";
|
||||
public const string PackageUrlXpath = "/package/url";
|
||||
public const string PackageLicenseXpath = "/package/license";
|
||||
public const string AuthorNameXpath = "/author/name";
|
||||
public const string AuthorWebsiteXpath = "/author/website";
|
||||
public const string ReadmeXpath = "/readme";
|
||||
public const string ControlNodeName = "control";
|
||||
public const string ActionNodeName = "Action";
|
||||
public const string ActionsNodeName = "Actions";
|
||||
public const string UndoNodeAttribute = "undo";
|
||||
public const string RunatNodeAttribute = "runat";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,12 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string FolderBrowserAlias = "Umbraco.FolderBrowser";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the grid datatype.
|
||||
/// </summary>
|
||||
public const string GridAlias = "Umbraco.Grid";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Image Cropper datatype.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,47 +1,35 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId)
|
||||
: base(original, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId) : base(original, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId)
|
||||
: base(eventObject)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, int parentId) : base(eventObject)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
public CopyEventArgs(TEntity eventObject, TEntity copy, bool canCancel, int parentId, bool relateToOriginal)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
RelateToOriginal = relateToOriginal;
|
||||
}
|
||||
/// <summary>
|
||||
/// The copied entity
|
||||
/// </summary>
|
||||
public TEntity Copy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The copied entity
|
||||
/// </summary>
|
||||
public TEntity Copy { get; set; }
|
||||
/// <summary>
|
||||
/// The original entity
|
||||
/// </summary>
|
||||
public TEntity Original
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The original entity
|
||||
/// </summary>
|
||||
public TEntity Original
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
|
||||
public bool RelateToOriginal { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
{
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.Migrations;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IEnumerable<IMigration>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ConfiguredVersion = configuredVersion;
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
internal MigrationEventArgs(IList<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
internal MigrationEventArgs(IEnumerable<IMigration> eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
MigrationContext = migrationContext;
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion)
|
||||
public MigrationEventArgs(IEnumerable<IMigration> eventObject, Version configuredVersion, Version targetVersion)
|
||||
: base(eventObject)
|
||||
{
|
||||
ConfiguredVersion = configuredVersion;
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Returns all migrations that were used in the migration runner
|
||||
/// </summary>
|
||||
public IList<IMigration> Migrations
|
||||
public IEnumerable<IMigration> Migrations
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -18,10 +19,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static void CopyFile(this IFileSystem fs, string path, string newPath)
|
||||
{
|
||||
using (var stream = fs.OpenFile(path))
|
||||
{
|
||||
fs.AddFile(newPath, stream);
|
||||
}
|
||||
fs.AddFile(newPath, fs.OpenFile(path));
|
||||
}
|
||||
|
||||
public static string GetExtension(this IFileSystem fs, string path)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
|
||||
@@ -3,7 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -90,9 +93,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideIfExists)
|
||||
{
|
||||
var exists = FileExists(path);
|
||||
if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
if (FileExists(path) && !overrideIfExists) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
EnsureDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
if (stream.CanSeek)
|
||||
|
||||
@@ -154,18 +154,19 @@ namespace Umbraco.Core.IO
|
||||
EnsureFileSupportsResizing();
|
||||
|
||||
using (var fs = _fs.OpenFile(Path))
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
_size = new Size(fileWidth, fileHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = new Size(-1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _size.Value;
|
||||
}
|
||||
@@ -178,7 +179,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
var fileNameThumb = DoResize(width, height, -1, string.Empty);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -191,7 +192,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
var fileNameThumb = DoResize(-1, -1, maxWidthHeight, fileNameAddition);
|
||||
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
return _fs.GetUrl(fileNameThumb);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -205,14 +206,14 @@ namespace Umbraco.Core.IO
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
|
||||
var thumbnail = maxWidthHeight == -1
|
||||
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
|
||||
: ImageHelper.GenerateThumbnail(image, maxWidthHeight, fileNameThumb, Extension, _fs);
|
||||
|
||||
return thumbnail.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileSupportsResizing()
|
||||
@@ -221,6 +222,6 @@ namespace Umbraco.Core.IO
|
||||
throw new InvalidOperationException(string.Format("The file {0} is not an image, so can't get dimensions", Filename));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace Umbraco.Core.Manifest
|
||||
internal class ManifestWatcher : DisposableObject
|
||||
{
|
||||
private readonly List<FileSystemWatcher> _fws = new List<FileSystemWatcher>();
|
||||
private static volatile bool _isRestarting = false;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
public void Start(params string[] packageFolders)
|
||||
{
|
||||
@@ -34,21 +32,9 @@ namespace Umbraco.Core.Manifest
|
||||
{
|
||||
if (e.Name.InvariantContains("package.manifest"))
|
||||
{
|
||||
//Ensure the app is not restarted multiple times for multiple saving during the same app domain execution
|
||||
if (_isRestarting == false)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_isRestarting == false)
|
||||
{
|
||||
_isRestarting = true;
|
||||
|
||||
LogHelper.Info<ManifestWatcher>("manifest has changed, app pool is restarting (" + e.FullPath + ")");
|
||||
HttpRuntime.UnloadAppDomain();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
LogHelper.Info<ManifestWatcher>("manifest has changed, app pool is restarting (" + e.FullPath + ")");
|
||||
HttpRuntime.UnloadAppDomain();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,16 +165,6 @@ namespace Umbraco.Core.Media
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
}
|
||||
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", string.Format("{0}x{1}", widthTh, heightTh));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
|
||||
@@ -240,28 +240,6 @@ namespace Umbraco.Core.Models
|
||||
return content.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Contains(recycleBinId.ToInvariantString());
|
||||
}
|
||||
|
||||
/// <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
|
||||
@@ -612,12 +590,6 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias);
|
||||
}
|
||||
property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup);
|
||||
}
|
||||
|
||||
internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
|
||||
{
|
||||
if (property == null) throw new ArgumentNullException("property");
|
||||
|
||||
var trimmedTags = tags.Select(x => x.Trim()).ToArray();
|
||||
|
||||
@@ -756,6 +728,10 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -93,8 +93,17 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
if (ContentTypeCompositionExists(alias))
|
||||
{
|
||||
var contentTypeComposition = ContentTypeComposition.First(x => x.Alias == alias);
|
||||
var contentTypeComposition = ContentTypeComposition.FirstOrDefault(x => x.Alias == alias);
|
||||
if (contentTypeComposition == null)//You can't remove a composition from another composition
|
||||
return false;
|
||||
|
||||
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
|
||||
|
||||
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
|
||||
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
|
||||
if(compositionIdsToRemove.Any())
|
||||
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
|
||||
|
||||
OnPropertyChanged(ContentTypeCompositionSelector);
|
||||
return _contentTypeComposition.Remove(contentTypeComposition);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -28,11 +27,6 @@ namespace Umbraco.Core.Models
|
||||
|
||||
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>
|
||||
@@ -49,18 +43,16 @@ namespace Umbraco.Core.Models
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
var refProperties = PropCache.GetOrAdd(inputType, type =>
|
||||
inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
x.GetCustomAttribute<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());
|
||||
var refProperties = inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
x.GetCustomAttribute<DoNotCloneAttribute>() == null
|
||||
//reference type but not string
|
||||
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false);
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -20,5 +23,5 @@ namespace Umbraco.Core.Models
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Tag, which is composed of a Text, Group and NodeCount property.
|
||||
/// </summary>
|
||||
public interface ITag : IAggregateRoot
|
||||
{
|
||||
[DataMember]
|
||||
@@ -14,8 +11,6 @@ namespace Umbraco.Core.Models
|
||||
[DataMember]
|
||||
string Group { get; set; }
|
||||
|
||||
int NodeCount { get; }
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//int ParentId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user -> entity permission
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entity -> user & permission key value pair collection
|
||||
/// </summary>
|
||||
public class EntityPermissionSet
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity id with permissions assigned
|
||||
/// </summary>
|
||||
public int EntityId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key/value pairs of user id & single permission
|
||||
/// </summary>
|
||||
public IEnumerable<UserPermission> UserPermissionsSet { get; private set; }
|
||||
|
||||
public EntityPermissionSet(int entityId, IEnumerable<UserPermission> userPermissionsSet)
|
||||
{
|
||||
EntityId = entityId;
|
||||
UserPermissionsSet = userPermissionsSet;
|
||||
}
|
||||
|
||||
public class UserPermission
|
||||
{
|
||||
public UserPermission(int userId, string permission)
|
||||
{
|
||||
UserId = userId;
|
||||
Permission = permission;
|
||||
}
|
||||
|
||||
public int UserId { get; private set; }
|
||||
public string Permission { get; private set; }
|
||||
|
||||
protected bool Equals(UserPermission other)
|
||||
{
|
||||
return UserId == other.UserId && string.Equals(Permission, other.Permission);
|
||||
}
|
||||
|
||||
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((UserPermission) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (UserId*397) ^ Permission.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the base contract for <see cref="IMember"/> and <see cref="IUser"/>
|
||||
/// </summary>
|
||||
public interface IMembershipUser : IAggregateRoot
|
||||
{
|
||||
object ProviderUserKey { get; set; }
|
||||
|
||||
@@ -1,55 +1,27 @@
|
||||
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>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserType : Entity, IUserType
|
||||
{
|
||||
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 Alias { get; set; }
|
||||
|
||||
[DataMember]
|
||||
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);
|
||||
}
|
||||
}
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The set of default permissions for the user type
|
||||
@@ -58,17 +30,6 @@ 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 { return _permissions; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_permissions = value;
|
||||
return _permissions;
|
||||
}, _permissions, PermissionsSelector);
|
||||
}
|
||||
}
|
||||
public IEnumerable<string> Permissions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Partial View file
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PartialView : File
|
||||
{
|
||||
private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
|
||||
public PartialView(string path)
|
||||
: base(path)
|
||||
{
|
||||
base.Path = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the file could be validated
|
||||
/// </summary>
|
||||
/// <returns>True if file is valid, otherwise false</returns>
|
||||
public override bool IsValid()
|
||||
{
|
||||
//TODO: Validate using the macro engine
|
||||
//var engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName);
|
||||
//engine.Validate(...)
|
||||
|
||||
var validatePath = IOHelper.ValidateEditPath(IOHelper.MapPath(Path), BasePath);
|
||||
var verifyFileExtension = IOHelper.VerifyFileExtension(Path, new List<string> { "cshtml" });
|
||||
|
||||
return validatePath && verifyFileExtension;
|
||||
}
|
||||
|
||||
public string OldFileName { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public string SnippetName { get; set; }
|
||||
|
||||
public bool CreateMacro { get; set; }
|
||||
|
||||
public string CodeHeader { get; set; }
|
||||
|
||||
public string ParentFolderName { get; set; }
|
||||
|
||||
public string EditViewFile { get; set; }
|
||||
|
||||
public string BasePath { get; set; }
|
||||
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
internal Regex HeaderMatch
|
||||
{
|
||||
get { return _headerMatch; }
|
||||
}
|
||||
|
||||
internal Attempt<string> TryGetSnippetPath(string fileName)
|
||||
{
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(BasePath);
|
||||
var snippetPath = IOHelper.MapPath(string.Format("{0}/PartialViewMacros/Templates/{1}", SystemDirectories.Umbraco, fileName));
|
||||
|
||||
return partialViewsFileSystem.FileExists(snippetPath)
|
||||
? Attempt<string>.Succeed(snippetPath)
|
||||
: Attempt<string>.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,6 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
// first unwrap content down to the lowest possible level, ie either the deepest inner
|
||||
// IPublishedContent or the first extended that has added properties. this is to avoid
|
||||
// nesting extended objects as much as possible, so we try to re-extend that lowest
|
||||
// object.
|
||||
|
||||
var wrapped = content as PublishedContentExtended;
|
||||
while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
|
||||
wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
|
||||
@@ -56,38 +51,14 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
// a model, and then that model has to inherit from PublishedContentExtended,
|
||||
// => implements the internal IPublishedContentExtended.
|
||||
|
||||
// here we assume that either the factory just created a model that implements
|
||||
// IPublishedContentExtended and therefore does not need to be extended again,
|
||||
// because it can carry the extra property - or that it did *not* create a
|
||||
// model and therefore returned the original content unchanged.
|
||||
|
||||
var model = content.CreateModel();
|
||||
var extended = model == content // == means the factory did not create a model
|
||||
? new PublishedContentExtended(content) // so we have to extend
|
||||
: model; // else we can use what the factory returned
|
||||
|
||||
// so extended should always implement IPublishedContentExtended, however if
|
||||
// by mistake the factory returned a different object that does not implement
|
||||
// IPublishedContentExtended (which would be an error), throw.
|
||||
//
|
||||
// see also PublishedContentExtensionsForModels.CreateModel
|
||||
|
||||
// NOTE
|
||||
// could we lift that constraint and accept that models just be IPublishedContent?
|
||||
// would then mean that we cannot assume a model is IPublishedContentExtended, so
|
||||
// either it is, or we need to wrap it. so instead of having
|
||||
// (Model:IPublishedContentExtended (IPublishedContent))
|
||||
// we'd have
|
||||
// (PublishedContentExtended (Model (IPublishedContent)))
|
||||
// and it is that bad? any other consequences?
|
||||
//
|
||||
// would also allow the factory to cache the model (though that should really
|
||||
// be done by the content cache, not by the factory).
|
||||
|
||||
var extended2 = extended as IPublishedContentExtended;
|
||||
if (extended2 == null)
|
||||
throw new Exception("Extended does not implement IPublishedContentExtended.");
|
||||
extended2.SetContentSet(contentSet);
|
||||
if (extended2 != null) // always true, but keeps Resharper happy
|
||||
extended2.SetContentSet(contentSet);
|
||||
return extended2;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
@@ -14,32 +12,9 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(this IPublishedContent content)
|
||||
{
|
||||
if (content == null)
|
||||
return null;
|
||||
|
||||
if (PublishedContentModelFactoryResolver.Current.HasValue == false)
|
||||
return content;
|
||||
|
||||
// get model
|
||||
// if factory returns nothing, throw
|
||||
// if factory just returns what it got, return
|
||||
var model = PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content);
|
||||
if (model == null)
|
||||
throw new Exception("IPublishedContentFactory returned null.");
|
||||
if (ReferenceEquals(model, content))
|
||||
return content;
|
||||
|
||||
// at the moment, other parts of our code assume that all models will
|
||||
// somehow implement IPublishedContentExtended and not just be IPublishedContent,
|
||||
// so we'd better check this here to fail as soon as we can.
|
||||
//
|
||||
// see also PublishedContentExtended.Extend
|
||||
|
||||
var extended = model as IPublishedContentExtended;
|
||||
if (extended == null)
|
||||
throw new Exception("IPublishedContentFactory created an object that does not implement IPublishedContentModelExtended.");
|
||||
|
||||
return model;
|
||||
return PublishedContentModelFactoryResolver.Current.HasValue
|
||||
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
public PublishedContentModelFactory(IEnumerable<Type> types)
|
||||
{
|
||||
var ctorArgTypes = new[] { typeof(IPublishedContent) };
|
||||
var constructors = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
var constructors = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
@@ -40,6 +40,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
|
||||
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
typeName = typeName.ToLowerInvariant();
|
||||
|
||||
if (constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
@@ -69,7 +70,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
return content;
|
||||
|
||||
// be case-insensitive
|
||||
var contentTypeAlias = content.DocumentTypeAlias;
|
||||
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
|
||||
|
||||
//ConstructorInfo constructor;
|
||||
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
|
||||
@@ -78,23 +78,5 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool Equals(PropertyDataDto other)
|
||||
{
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
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((PropertyDataDto) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,5 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(100)]
|
||||
public string Group { get; set; }//NOTE Is set to [varchar] (100) in Sql Server script
|
||||
|
||||
[ResultColumn("NodeCount")]
|
||||
public int NodeCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
public int EntityId { get; private set; }
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<Tag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
public int PropertyTypeId { get; private set; }
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
public IEnumerable<Tag> Tags { get; private set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Tag : Entity, ITag
|
||||
@@ -16,15 +43,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public Tag(int id, string text, string @group)
|
||||
{
|
||||
Id = id;
|
||||
Text = text;
|
||||
Group = @group;
|
||||
}
|
||||
|
||||
public Tag(int id, string text, string @group, int nodeCount)
|
||||
: this(id, text, @group)
|
||||
{
|
||||
NodeCount = nodeCount;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Text);
|
||||
@@ -58,8 +79,6 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public int NodeCount { get; internal set; }
|
||||
|
||||
//TODO: enable this at some stage
|
||||
//public int ParentId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a tagged entity.
|
||||
/// </summary>
|
||||
/// <remarks>Note that it is the properties of an entity (like Content, Media, Members, etc.) that is tagged,
|
||||
/// which is why this class is composed of a list of tagged properties and an Id reference to the actual entity.</remarks>
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the entity, which is tagged
|
||||
/// </summary>
|
||||
public int EntityId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An enumerable list of tagged properties
|
||||
/// </summary>
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a tagged property on an entity.
|
||||
/// </summary>
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<ITag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id of the PropertyType, which this tagged property is based on
|
||||
/// </summary>
|
||||
public int PropertyTypeId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Alias of the PropertyType, which this tagged property is based on
|
||||
/// </summary>
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An enumerable list of Tags for the property
|
||||
/// </summary>
|
||||
public IEnumerable<ITag> Tags { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -87,13 +87,13 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(FriendlyNameAttribute),
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
|
||||
false);
|
||||
|
||||
if (attributes.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
var attribute = ((FriendlyNameAttribute)attributes[0]);
|
||||
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return string.Empty;
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
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,11 +63,6 @@ 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>
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class ConflictingPackageData : IConflictingPackageData
|
||||
{
|
||||
private readonly IMacroService _macroService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
public ConflictingPackageData(IMacroService macroService,
|
||||
IFileService fileService)
|
||||
{
|
||||
if (fileService != null) _fileService = fileService;
|
||||
else throw new ArgumentNullException("fileService");
|
||||
if (macroService != null) _macroService = macroService;
|
||||
else throw new ArgumentNullException("macroService");
|
||||
}
|
||||
|
||||
public IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("the root element must be \"" + Constants.Packaging.StylesheetsNodeName + "\"", "stylesheetNotes");
|
||||
}
|
||||
|
||||
return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.NameNodeName);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element",
|
||||
"stylesheetNotes");
|
||||
}
|
||||
|
||||
return _fileService.GetStylesheetByName(xElement.Value) as IFile;
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node",
|
||||
"templateNotes");
|
||||
}
|
||||
|
||||
return templateNotes.Elements(Constants.Packaging.TemplateNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeNameCapital + "\" element",
|
||||
"templateNotes");
|
||||
}
|
||||
|
||||
return _fileService.GetTemplate(xElement.Value);
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
|
||||
public IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.MacrosNodeName, macroNodes.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Node must be a \"" + Constants.Packaging.MacrosNodeName + "\" node",
|
||||
"macroNodes");
|
||||
}
|
||||
|
||||
return macroNodes.Elements(Constants.Packaging.MacroNodeName)
|
||||
.Select(n =>
|
||||
{
|
||||
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital);
|
||||
if (xElement == null)
|
||||
{
|
||||
throw new ArgumentException(string.Format("missing a \"{0}\" element in {0} element", Constants.Packaging.AliasNodeNameSmall),
|
||||
"macroNodes");
|
||||
}
|
||||
|
||||
return _macroService.GetByAlias(xElement.Value);
|
||||
})
|
||||
.Where(v => v != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IConflictingPackageData
|
||||
{
|
||||
IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes);
|
||||
IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes);
|
||||
IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to access an umbraco package file
|
||||
/// Remeber that filenames must be unique
|
||||
/// use "FindDubletFileNames" for sanitycheck
|
||||
/// </summary>
|
||||
internal interface IPackageExtraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the content of the file with the given filename
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <param name="fileToRead">filename of the file for wich to get the text content</param>
|
||||
/// <param name="directoryInPackage">this is the relative directory for the location of the file in the package
|
||||
/// I dont know why umbraco packages contains directories in the first place??</param>
|
||||
/// <returns>text content of the file</returns>
|
||||
string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a file from package to given destination
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the ubraco package file</param>
|
||||
/// <param name="fileInPackageName">filename of the file to copy</param>
|
||||
/// <param name="destinationfilePath">destination path (including destination filename)</param>
|
||||
/// <returns>True a file was overwritten</returns>
|
||||
void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a file from package to given destination
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the ubraco package file</param>
|
||||
/// <param name="sourceDestination">Key: Source file in package. Value: Destination path inclusive file name</param>
|
||||
void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination);
|
||||
|
||||
/// <summary>
|
||||
/// Check if given list of files can be found in the package
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <param name="expectedFiles">a list of files you would like to find in the package</param>
|
||||
/// <returns>a subset if any of the files in "expectedFiles" that could not be found in the package</returns>
|
||||
IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sanitycheck - should return en empty collection if package is valid
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath">Full path to the umbraco package file</param>
|
||||
/// <returns>list of files that are found more than ones (accross directories) in the package</returns>
|
||||
IEnumerable<string> FindDubletFileNames(string packageFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the given files from archive and returns them as a collection of byte arrays
|
||||
/// </summary>
|
||||
/// <param name="packageFilePath"></param>
|
||||
/// <param name="filesToGet"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageInstallation
|
||||
{
|
||||
InstallationSummary InstallPackage(string packageFilePath, int userId);
|
||||
MetaData GetMetaData(string packageFilePath);
|
||||
PreInstallWarnings GetPreInstallWarnings(string packageFilePath);
|
||||
XElement GetConfigXmlElement(string packageFilePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallAction
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public string RunAt { get; set; }//NOTE Should this default to "install"
|
||||
|
||||
public bool Undo { get; set; }
|
||||
|
||||
public XmlNode XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<IDataTypeDefinition> DataTypesInstalled { get; set; }
|
||||
public IEnumerable<ILanguage> LanguagesInstalled { get; set; }
|
||||
public IEnumerable<IDictionaryItem> DictionaryItemsInstalled { get; set; }
|
||||
public IEnumerable<IMacro> MacrosInstalled { get; set; }
|
||||
public IEnumerable<string> FilesInstalled { get; set; }
|
||||
public IEnumerable<ITemplate> TemplatesInstalled { get; set; }
|
||||
public IEnumerable<IContentType> ContentTypesInstalled { get; set; }
|
||||
public IEnumerable<IFile> StylesheetsInstalled { get; set; }
|
||||
public IEnumerable<IContent> ContentInstalled { get; set; }
|
||||
public IEnumerable<PackageAction> Actions { get; set; }
|
||||
public bool PackageInstalled { get; set; }
|
||||
}
|
||||
|
||||
internal static class InstallationSummaryExtentions
|
||||
{
|
||||
public static InstallationSummary InitEmpty(this InstallationSummary summary)
|
||||
{
|
||||
summary.Actions = new List<PackageAction>();
|
||||
summary.ContentInstalled = new List<IContent>();
|
||||
summary.ContentTypesInstalled = new List<IContentType>();
|
||||
summary.DataTypesInstalled = new List<IDataTypeDefinition>();
|
||||
summary.DictionaryItemsInstalled = new List<IDictionaryItem>();
|
||||
summary.FilesInstalled = new List<string>();
|
||||
summary.LanguagesInstalled = new List<ILanguage>();
|
||||
summary.MacrosInstalled = new List<IMacro>();
|
||||
summary.MetaData = new MetaData();
|
||||
summary.TemplatesInstalled = new List<ITemplate>();
|
||||
summary.PackageInstalled = false;
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class InstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<int> DataTypesInstalled { get; set; }
|
||||
public IEnumerable<int> LanguagesInstalled { get; set; }
|
||||
public IEnumerable<int> DictionaryItemsInstalled { get; set; }
|
||||
public IEnumerable<int> MacrosInstalled { get; set; }
|
||||
public IEnumerable<KeyValuePair<string, bool>> FilesInstalled { get; set;}
|
||||
public IEnumerable<int> TemplatesInstalled { get; set; }
|
||||
public IEnumerable<int> DocumentTypesInstalled { get; set; }
|
||||
public IEnumerable<int> StylesheetsInstalled { get; set; }
|
||||
public IEnumerable<int> DocumentsInstalled { get; set; }
|
||||
public IEnumerable<InstallAction> InstallActions { get; set; }
|
||||
public IEnumerable<UninstallAction> UninstallActions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
internal enum ActionRunAt
|
||||
{
|
||||
Undefined = 0,
|
||||
Install,
|
||||
Uninstall
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PackageAction
|
||||
{
|
||||
private ActionRunAt _runAt;
|
||||
private bool? _undo;
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public ActionRunAt RunAt
|
||||
{
|
||||
get { return _runAt == ActionRunAt.Undefined ? ActionRunAt.Install : _runAt; }
|
||||
set { _runAt = value; }
|
||||
}
|
||||
|
||||
public bool Undo //NOTE: Should thid default to "False"? but the documentation says default "True" (http://our.umbraco.org/wiki/reference/packaging/package-actions)
|
||||
{
|
||||
get { return _undo ?? true; }
|
||||
set { _undo = value; }
|
||||
}
|
||||
|
||||
public XElement XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class PreInstallWarnings
|
||||
{
|
||||
public KeyValuePair<string, string>[] UnsecureFiles { get; set; }
|
||||
public KeyValuePair<string, string>[] FilesReplaced { get; set; }
|
||||
public IEnumerable<IMacro> ConflictingMacroAliases { get; set; }
|
||||
public IEnumerable<ITemplate> ConflictingTemplateAliases { get; set; }
|
||||
public IEnumerable<IFile> ConflictingStylesheetNames { get; set; }
|
||||
public IEnumerable<string> AssembliesWithLegacyPropertyEditors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UninstallAction
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public string RunAt { get; set; }//NOTE Should this default to "install"
|
||||
|
||||
public bool Undo { get; set; }//NOTE: Should thid default to "False"?
|
||||
|
||||
public XmlNode XmlData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,47 +5,15 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
// Note
|
||||
// That class uses ReflectionOnlyLoad which does NOT handle policies (bindingRedirect) and
|
||||
// therefore raised warnings when installing a package, if an exact dependency could not be
|
||||
// found, though it would be found via policies. So we have to explicitely apply policies
|
||||
// where appropriate.
|
||||
|
||||
internal class PackageBinaryInspector : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry point to call from your code
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="assemblys"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Will perform the assembly scan in a separate app domain
|
||||
/// </remarks>
|
||||
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(IEnumerable<byte[]> assemblys, out string[] errorReport)
|
||||
{
|
||||
var appDomain = GetTempAppDomain();
|
||||
var type = typeof(PackageBinaryInspector);
|
||||
try
|
||||
{
|
||||
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
// do NOT turn PerformScan into static (even if ReSharper says so)!
|
||||
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
AppDomain.Unload(appDomain);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry point to call from your code
|
||||
/// </summary>
|
||||
@@ -65,7 +33,6 @@ namespace Umbraco.Core.Packaging
|
||||
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
// do NOT turn PerformScan into static (even if ReSharper says so)!
|
||||
var result = value.PerformScan<T>(dllPath, out errorReport);
|
||||
return result;
|
||||
}
|
||||
@@ -75,35 +42,6 @@ namespace Umbraco.Core.Packaging
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the assembly scanning
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This method is executed in a separate app domain
|
||||
/// </remarks>
|
||||
private IEnumerable<string> PerformScan<T>(IEnumerable<byte[]> assemblies, out string[] errorReport)
|
||||
{
|
||||
//we need this handler to resolve assembly dependencies when loading below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
|
||||
var a = Assembly.ReflectionOnlyLoad(name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
// do NOT apply policy here: we want to scan the dlls that are in the binaries
|
||||
var loaded = assemblies.Select(Assembly.ReflectionOnlyLoad).ToList();
|
||||
|
||||
//scan
|
||||
return PerformScan<T>(loaded, out errorReport);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the assembly scanning
|
||||
/// </summary>
|
||||
@@ -114,42 +52,33 @@ namespace Umbraco.Core.Packaging
|
||||
/// <remarks>
|
||||
/// This method is executed in a separate app domain
|
||||
/// </remarks>
|
||||
private IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
internal IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
{
|
||||
if (Directory.Exists(dllPath) == false)
|
||||
{
|
||||
throw new DirectoryNotFoundException("Could not find directory " + dllPath);
|
||||
}
|
||||
|
||||
//we need this handler to resolve assembly dependencies when loading below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
|
||||
var a = Assembly.ReflectionOnlyLoad(name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
// do NOT apply policy here: we want to scan the dlls that are in the path
|
||||
var files = Directory.GetFiles(dllPath, "*.dll");
|
||||
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
|
||||
|
||||
//scan
|
||||
return PerformScan<T>(loaded, out errorReport);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> PerformScan<T>(IList<Assembly> loaded, out string[] errorReport)
|
||||
{
|
||||
var dllsWithReference = new List<string>();
|
||||
var errors = new List<string>();
|
||||
var assembliesWithErrors = new List<Assembly>();
|
||||
|
||||
//we need this handler to resolve assembly dependencies below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var a = Assembly.ReflectionOnlyLoad(e.Name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
|
||||
|
||||
//load each of the LoadFrom assemblies into the Load context too
|
||||
foreach (var a in loaded)
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(a.FullName);
|
||||
Assembly.ReflectionOnlyLoad(name);
|
||||
Assembly.ReflectionOnlyLoad(a.FullName);
|
||||
}
|
||||
|
||||
//get the list of assembly names to compare below
|
||||
@@ -165,8 +94,7 @@ namespace Umbraco.Core.Packaging
|
||||
{
|
||||
try
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(assemblyName.FullName);
|
||||
Assembly.ReflectionOnlyLoad(name);
|
||||
Assembly.ReflectionOnlyLoad(assemblyName.FullName);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
@@ -239,6 +167,7 @@ namespace Umbraco.Core.Packaging
|
||||
return dllsWithReference;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// In order to compare types, the types must be in the same context, this method will return the type that
|
||||
/// we are checking against but from the Load context.
|
||||
@@ -247,8 +176,7 @@ namespace Umbraco.Core.Packaging
|
||||
/// <returns></returns>
|
||||
private static Type GetLoadFromContractType<T>()
|
||||
{
|
||||
var name = AppDomain.CurrentDomain.ApplyPolicy(typeof(T).Assembly.FullName);
|
||||
var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoad(name);
|
||||
var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName);
|
||||
|
||||
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
|
||||
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
|
||||
@@ -288,5 +216,13 @@ namespace Umbraco.Core.Packaging
|
||||
domainSetup,
|
||||
new PermissionSet(PermissionState.Unrestricted));
|
||||
}
|
||||
|
||||
private static string GetAssemblyPath(Assembly a)
|
||||
{
|
||||
var codeBase = a.CodeBase;
|
||||
var uri = new Uri(codeBase);
|
||||
return uri.LocalPath;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Packaging
|
||||
|
||||
internal class PackageBuilding : IPackageBuilding
|
||||
{
|
||||
private readonly IPackagingService _packagingService;
|
||||
private readonly PackagingService _packagingService;
|
||||
|
||||
public PackageBuilding(IPackagingService packagingService)
|
||||
public PackageBuilding(PackagingService packagingService)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
}
|
||||
|
||||
@@ -1,201 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class PackageExtraction : IPackageExtraction
|
||||
{
|
||||
public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage)
|
||||
{
|
||||
string retVal = null;
|
||||
bool fileFound = false;
|
||||
string foundDir = null;
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = Path.GetFileName(entry.Name);
|
||||
|
||||
if (string.IsNullOrEmpty(fileName) == false &&
|
||||
fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
|
||||
foundDir = entry.Name.Substring(0, entry.Name.Length - fileName.Length);
|
||||
fileFound = true;
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
retVal = reader.ReadToEnd();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (fileFound == false)
|
||||
{
|
||||
directoryInPackage = null;
|
||||
throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead);
|
||||
}
|
||||
directoryInPackage = foundDir;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static void CheckPackageExists(string packageFilePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(packageFilePath))
|
||||
{
|
||||
throw new ArgumentNullException("packageFilePath");
|
||||
}
|
||||
|
||||
if (File.Exists(packageFilePath) == false)
|
||||
{
|
||||
if (File.Exists(packageFilePath) == false)
|
||||
throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath));
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(packageFilePath).ToLower();
|
||||
|
||||
var alowedExtension = new[] { ".umb", ".zip" };
|
||||
|
||||
// Check if the file is a valid package
|
||||
if (alowedExtension.All(ae => ae.Equals(extension) == false))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension)));
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath)
|
||||
{
|
||||
CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair<string, string>(fileInPackageName, destinationfilePath) } );
|
||||
}
|
||||
|
||||
public void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
if (fileName == string.Empty) { return true; }
|
||||
|
||||
string destination;
|
||||
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
|
||||
{
|
||||
using (var streamWriter = File.Open(destination, FileMode.Create))
|
||||
{
|
||||
stream.CopyTo(streamWriter);
|
||||
}
|
||||
|
||||
d.Remove(fileName);
|
||||
return d.Any();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (d.Any())
|
||||
{
|
||||
throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles)
|
||||
{
|
||||
var retVal = expectedFiles.ToList();
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (zipEntry, stream) =>
|
||||
{
|
||||
string fileName = Path.GetFileName(zipEntry.Name);
|
||||
|
||||
int index = retVal.FindIndex(f => f.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (index != -1) { retVal.RemoveAt(index); }
|
||||
|
||||
return retVal.Any();
|
||||
});
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<string> FindDubletFileNames(string packageFilePath)
|
||||
{
|
||||
var dictionary = new Dictionary<string, List<string>>();
|
||||
|
||||
|
||||
ReadZipfileEntries(packageFilePath, (entry, stream) =>
|
||||
{
|
||||
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
|
||||
|
||||
List<string> list;
|
||||
if (dictionary.TryGetValue(fileName, out list) == false)
|
||||
{
|
||||
list = new List<string>();
|
||||
dictionary.Add(fileName, list);
|
||||
}
|
||||
|
||||
list.Add(entry.Name);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v);
|
||||
}
|
||||
|
||||
public IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet)
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
var files = new HashSet<string>(filesToGet.Select(f => f.ToLower()));
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
if (zipEntry.IsDirectory) continue;
|
||||
|
||||
if (files.Contains(zipEntry.Name))
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
zipInputStream.CopyTo(memStream);
|
||||
yield return memStream.ToArray();
|
||||
memStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadZipfileEntries(string packageFilePath, Func<ZipEntry, ZipInputStream, bool> entryFunc, bool skipsDirectories = true)
|
||||
{
|
||||
CheckPackageExists(packageFilePath);
|
||||
|
||||
using (var fs = File.OpenRead(packageFilePath))
|
||||
{
|
||||
using (var zipInputStream = new ZipInputStream(fs))
|
||||
{
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (zipEntry.IsDirectory && skipsDirectories) continue;
|
||||
if (entryFunc(zipEntry, zipInputStream) == false) break;
|
||||
}
|
||||
|
||||
zipInputStream.Close();
|
||||
}
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageExtraction
|
||||
{
|
||||
bool Extract(string packageFilePath, string destinationFolder);
|
||||
string ExtractToTemporaryFolder(string packageFilePath);
|
||||
string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml");
|
||||
}
|
||||
|
||||
internal class PackageExtraction : IPackageExtraction
|
||||
{
|
||||
public bool Extract(string packageFilePath, string destinationFolder)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public string ExtractToTemporaryFolder(string packageFilePath)
|
||||
{
|
||||
string tempDir = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), Guid.NewGuid().ToString("D"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
Extract(packageFilePath, tempDir);
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
public string GetPackageConfigFromArchive(string packageFilePath, string fileToRead = "package.xml")
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,605 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class PackageInstallation : IPackageInstallation
|
||||
{
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IMacroService _macroService;
|
||||
private readonly IPackagingService _packagingService;
|
||||
private IConflictingPackageData _conflictingPackageData;
|
||||
private readonly IPackageExtraction _packageExtraction;
|
||||
private string _fullPathToRoot;
|
||||
|
||||
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
|
||||
IFileService fileService, IPackageExtraction packageExtraction)
|
||||
: this(packagingService, macroService, fileService, packageExtraction, GlobalSettings.FullpathToRoot)
|
||||
{}
|
||||
|
||||
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
|
||||
IFileService fileService, IPackageExtraction packageExtraction, string fullPathToRoot)
|
||||
{
|
||||
if (packageExtraction != null) _packageExtraction = packageExtraction;
|
||||
else throw new ArgumentNullException("packageExtraction");
|
||||
|
||||
if (macroService != null) _macroService = macroService;
|
||||
else throw new ArgumentNullException("macroService");
|
||||
|
||||
if (fileService != null) _fileService = fileService;
|
||||
else throw new ArgumentNullException("fileService");
|
||||
|
||||
if (packagingService != null) _packagingService = packagingService;
|
||||
else throw new ArgumentNullException("packagingService");
|
||||
|
||||
_fullPathToRoot = fullPathToRoot;
|
||||
}
|
||||
|
||||
public IConflictingPackageData ConflictingPackageData
|
||||
{
|
||||
private get
|
||||
{
|
||||
return _conflictingPackageData ??
|
||||
(_conflictingPackageData = new ConflictingPackageData(_macroService, _fileService));
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_conflictingPackageData != null)
|
||||
{
|
||||
throw new PropertyConstraintException("This property already have a value");
|
||||
}
|
||||
_conflictingPackageData = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string FullPathToRoot
|
||||
{
|
||||
private get { return _fullPathToRoot; }
|
||||
set
|
||||
{
|
||||
|
||||
if (_fullPathToRoot != null)
|
||||
{
|
||||
throw new PropertyConstraintException("This property already have a value");
|
||||
}
|
||||
|
||||
_fullPathToRoot = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MetaData GetMetaData(string packageFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
return GetMetaData(rootElement);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public PreInstallWarnings GetPreInstallWarnings(string packageFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
return GetPreInstallWarnings(packageFilePath, rootElement);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFilePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public InstallationSummary InstallPackage(string packageFile, int userId)
|
||||
{
|
||||
XElement dataTypes;
|
||||
XElement languages;
|
||||
XElement dictionaryItems;
|
||||
XElement macroes;
|
||||
XElement files;
|
||||
XElement templates;
|
||||
XElement documentTypes;
|
||||
XElement styleSheets;
|
||||
XElement documentSet;
|
||||
XElement documents;
|
||||
XElement actions;
|
||||
MetaData metaData;
|
||||
InstallationSummary installationSummary;
|
||||
|
||||
try
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFile);
|
||||
PackageSupportedCheck(rootElement);
|
||||
PackageStructureSanetyCheck(packageFile, rootElement);
|
||||
dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName);
|
||||
languages = rootElement.Element(Constants.Packaging.LanguagesNodeName);
|
||||
dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName);
|
||||
macroes = rootElement.Element(Constants.Packaging.MacrosNodeName);
|
||||
files = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
|
||||
documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName);
|
||||
styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName);
|
||||
documents = rootElement.Element(Constants.Packaging.DocumentsNodeName);
|
||||
actions = rootElement.Element(Constants.Packaging.ActionsNodeName);
|
||||
|
||||
metaData = GetMetaData(rootElement);
|
||||
installationSummary = new InstallationSummary {MetaData = metaData};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error reading " + packageFile, e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var dataTypeDefinitions = EmptyEnumerableIfNull<IDataTypeDefinition>(dataTypes) ?? InstallDataTypes(dataTypes, userId);
|
||||
installationSummary.DataTypesInstalled = dataTypeDefinitions;
|
||||
|
||||
var languagesInstalled = EmptyEnumerableIfNull<ILanguage>(languages) ?? InstallLanguages(languages, userId);
|
||||
installationSummary.LanguagesInstalled = languagesInstalled;
|
||||
|
||||
var dictionaryInstalled = EmptyEnumerableIfNull<IDictionaryItem>(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems);
|
||||
installationSummary.DictionaryItemsInstalled = dictionaryInstalled;
|
||||
|
||||
var macros = EmptyEnumerableIfNull<IMacro>(macroes) ?? InstallMacros(macroes, userId);
|
||||
installationSummary.MacrosInstalled = macros;
|
||||
|
||||
var keyValuePairs = EmptyEnumerableIfNull<string>(packageFile) ?? InstallFiles(packageFile, files);
|
||||
installationSummary.FilesInstalled = keyValuePairs;
|
||||
|
||||
var templatesInstalled = EmptyEnumerableIfNull<ITemplate>(templates) ?? InstallTemplats(templates, userId);
|
||||
installationSummary.TemplatesInstalled = templatesInstalled;
|
||||
|
||||
var documentTypesInstalled = EmptyEnumerableIfNull<IContentType>(documentTypes) ?? InstallDocumentTypes(documentTypes, userId);
|
||||
installationSummary.ContentTypesInstalled =documentTypesInstalled;
|
||||
|
||||
var stylesheetsInstalled = EmptyEnumerableIfNull<IFile>(styleSheets) ?? InstallStylesheets(styleSheets, userId);
|
||||
installationSummary.StylesheetsInstalled = stylesheetsInstalled;
|
||||
|
||||
var documentsInstalled = documents != null ? InstallDocuments(documents, userId)
|
||||
: EmptyEnumerableIfNull<IContent>(documentSet)
|
||||
?? InstallDocuments(documentSet, userId);
|
||||
installationSummary.ContentInstalled = documentsInstalled;
|
||||
|
||||
var packageActions = EmptyEnumerableIfNull<PackageAction>(actions) ?? GetPackageActions(actions, metaData.Name);
|
||||
installationSummary.Actions = packageActions;
|
||||
|
||||
installationSummary.PackageInstalled = true;
|
||||
|
||||
return installationSummary;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Error installing package " + packageFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temperary check to test that we support stylesheets
|
||||
/// </summary>
|
||||
/// <param name="rootElement"></param>
|
||||
private void PackageSupportedCheck(XElement rootElement)
|
||||
{
|
||||
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
if (styleSheets != null && styleSheets.Elements().Any())
|
||||
throw new NotSupportedException("Stylesheets is not suported in this version of umbraco");
|
||||
}
|
||||
|
||||
private static T[] EmptyArrayIfNull<T>(object obj)
|
||||
{
|
||||
return obj == null ? new T[0] : null;
|
||||
}
|
||||
|
||||
private static IEnumerable<T> EmptyEnumerableIfNull<T>(object obj)
|
||||
{
|
||||
return obj == null ? Enumerable.Empty<T>() : null;
|
||||
}
|
||||
|
||||
private XDocument GetConfigXmlDoc(string packageFilePath)
|
||||
{
|
||||
string filePathInPackage;
|
||||
string configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFilePath,
|
||||
Constants.Packaging.PackageXmlFileName, out filePathInPackage);
|
||||
|
||||
return XDocument.Parse(configXmlContent);
|
||||
}
|
||||
|
||||
public XElement GetConfigXmlElement(string packageFilePath)
|
||||
{
|
||||
XDocument document = GetConfigXmlDoc(packageFilePath);
|
||||
if (document.Root == null ||
|
||||
document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false)
|
||||
{
|
||||
throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath);
|
||||
}
|
||||
return document.Root;
|
||||
}
|
||||
|
||||
internal void PackageStructureSanetyCheck(string packageFilePath)
|
||||
{
|
||||
XElement rootElement = GetConfigXmlElement(packageFilePath);
|
||||
PackageStructureSanetyCheck(packageFilePath, rootElement);
|
||||
}
|
||||
|
||||
private void PackageStructureSanetyCheck(string packageFilePath, XElement rootElement)
|
||||
{
|
||||
XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
if (filesElement != null)
|
||||
{
|
||||
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement).ToArray();
|
||||
|
||||
var missingFiles = _packageExtraction.FindMissingFiles(packageFilePath, sourceDestination.Select(i => i.Key)).ToArray();
|
||||
|
||||
if (missingFiles.Any())
|
||||
{
|
||||
throw new Exception("The following file(s) are missing in the package: " +
|
||||
string.Join(", ", missingFiles.Select(
|
||||
mf =>
|
||||
{
|
||||
var sd = sourceDestination.Single(fi => fi.Key == mf);
|
||||
return string.Format("source: \"{0}\" destination: \"{1}\"",
|
||||
sd.Key, sd.Value);
|
||||
})));
|
||||
}
|
||||
|
||||
IEnumerable<string> dubletFileNames = _packageExtraction.FindDubletFileNames(packageFilePath).ToArray();
|
||||
|
||||
if (dubletFileNames.Any())
|
||||
{
|
||||
throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " +
|
||||
string.Join(", ", dubletFileNames));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<PackageAction> GetPackageActions(XElement actionsElement, string packageName)
|
||||
{
|
||||
if (actionsElement == null) { return new PackageAction[0]; }
|
||||
|
||||
if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root",
|
||||
"actionsElement");
|
||||
}
|
||||
|
||||
return actionsElement.Elements(Constants.Packaging.ActionNodeName)
|
||||
.Select(elemet =>
|
||||
{
|
||||
XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital);
|
||||
if (aliasAttr == null)
|
||||
throw new ArgumentException(
|
||||
"missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element",
|
||||
"actionsElement");
|
||||
|
||||
var packageAction = new PackageAction
|
||||
{
|
||||
XmlData = elemet,
|
||||
Alias = aliasAttr.Value,
|
||||
PackageName = packageName,
|
||||
};
|
||||
|
||||
|
||||
XAttribute attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute);
|
||||
|
||||
ActionRunAt runAt;
|
||||
if (attr != null && Enum.TryParse(attr.Value, true, out runAt)) { packageAction.RunAt = runAt; }
|
||||
|
||||
attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute);
|
||||
|
||||
bool undo;
|
||||
if (attr != null && bool.TryParse(attr.Value, out undo)) { packageAction.Undo = undo; }
|
||||
|
||||
|
||||
return packageAction;
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> InstallDocuments(XElement documentsElement, int userId = 0)
|
||||
{
|
||||
if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false)
|
||||
&& (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false))
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root",
|
||||
"documentsElement");
|
||||
}
|
||||
|
||||
if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName))
|
||||
return _packagingService.ImportContent(documentsElement, -1, userId);
|
||||
|
||||
return
|
||||
documentsElement.Elements(Constants.Packaging.DocumentSetNodeName)
|
||||
.SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IFile> InstallStylesheets(XElement styleSheetsElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root",
|
||||
"styleSheetsElement");
|
||||
}
|
||||
|
||||
// TODO: Call _packagingService when import stylesheets import has been implimentet
|
||||
if (styleSheetsElement.HasElements == false) { return new List<IFile>(); }
|
||||
|
||||
throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets");
|
||||
}
|
||||
|
||||
private IEnumerable<IContentType> InstallDocumentTypes(XElement documentTypes, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false)
|
||||
throw new ArgumentException(
|
||||
"Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes");
|
||||
|
||||
documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes);
|
||||
}
|
||||
|
||||
return _packagingService.ImportContentTypes(documentTypes, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<ITemplate> InstallTemplats(XElement templateElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root",
|
||||
"templateElement");
|
||||
}
|
||||
return _packagingService.ImportTemplates(templateElement, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<string> InstallFiles(string packageFilePath, XElement filesElement)
|
||||
{
|
||||
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement);
|
||||
sourceDestination = AppendRootToDestination(FullPathToRoot, sourceDestination);
|
||||
|
||||
_packageExtraction.CopyFilesFromArchive(packageFilePath, sourceDestination);
|
||||
|
||||
return sourceDestination.Select(sd => sd.Value).ToArray();
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] AppendRootToDestination(string fullpathToRoot, IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
return
|
||||
sourceDestination.Select(
|
||||
sd => new KeyValuePair<string, string>(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<IMacro> InstallMacros(XElement macroElements, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root",
|
||||
"macroElements");
|
||||
}
|
||||
return _packagingService.ImportMacros(macroElements, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<IDictionaryItem> InstallDictionaryItems(XElement dictionaryItemsElement)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) ==
|
||||
false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root",
|
||||
"dictionaryItemsElement");
|
||||
}
|
||||
return _packagingService.ImportDictionaryItems(dictionaryItemsElement);
|
||||
}
|
||||
|
||||
private IEnumerable<ILanguage> InstallLanguages(XElement languageElement, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement");
|
||||
}
|
||||
return _packagingService.ImportLanguages(languageElement, userId);
|
||||
}
|
||||
|
||||
private IEnumerable<IDataTypeDefinition> InstallDataTypes(XElement dataTypeElements, int userId = 0)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements");
|
||||
}
|
||||
}
|
||||
return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId);
|
||||
}
|
||||
|
||||
private PreInstallWarnings GetPreInstallWarnings(string packagePath, XElement rootElement)
|
||||
{
|
||||
XElement files = rootElement.Element(Constants.Packaging.FilesNodeName);
|
||||
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
|
||||
XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
|
||||
XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName);
|
||||
|
||||
var sourceDestination = EmptyArrayIfNull<KeyValuePair<string, string>>(files) ?? ExtractSourceDestinationFileInformation(files);
|
||||
|
||||
var installWarnings = new PreInstallWarnings();
|
||||
|
||||
var macroAliases = EmptyEnumerableIfNull<IMacro>(alias) ?? ConflictingPackageData.FindConflictingMacros(alias);
|
||||
installWarnings.ConflictingMacroAliases = macroAliases;
|
||||
|
||||
var templateAliases = EmptyEnumerableIfNull<ITemplate>(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates);
|
||||
installWarnings.ConflictingTemplateAliases = templateAliases;
|
||||
|
||||
var stylesheetNames = EmptyEnumerableIfNull<IFile>(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets);
|
||||
installWarnings.ConflictingStylesheetNames = stylesheetNames;
|
||||
|
||||
installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination);
|
||||
installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination);
|
||||
installWarnings.AssembliesWithLegacyPropertyEditors = FindLegacyPropertyEditors(packagePath, sourceDestination);
|
||||
|
||||
return installWarnings;
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] FindFilesToBeReplaced(IEnumerable<KeyValuePair<string, string>> sourceDestination)
|
||||
{
|
||||
return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray();
|
||||
}
|
||||
|
||||
private IEnumerable<string> FindLegacyPropertyEditors(string packagePath, IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
|
||||
{
|
||||
var dlls = sourceDestinationPair.Where(
|
||||
sd => (Path.GetExtension(sd.Value) ?? string.Empty).Equals(".dll", StringComparison.InvariantCultureIgnoreCase)).Select(sd => sd.Key).ToArray();
|
||||
|
||||
if (dlls.Any() == false) { return new List<string>(); }
|
||||
|
||||
// Now we want to see if the DLLs contain any legacy data types since we want to warn people about that
|
||||
string[] assemblyErrors;
|
||||
IEnumerable<byte[]> assemblyesToScan =_packageExtraction.ReadFilesFromArchive(packagePath, dlls);
|
||||
return PackageBinaryInspector.ScanAssembliesForTypeReference<IDataType>(assemblyesToScan, out assemblyErrors).ToArray();
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] FindUnsecureFiles(IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
|
||||
{
|
||||
return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.Value)).ToArray();
|
||||
}
|
||||
|
||||
private bool IsFileDestinationUnsecure(string destination)
|
||||
{
|
||||
var unsecureDirNames = new[] {"bin", "app_code"};
|
||||
if(unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase)))
|
||||
return true;
|
||||
|
||||
string extension = Path.GetExtension(destination);
|
||||
return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string>[] ExtractSourceDestinationFileInformation(XElement filesElement)
|
||||
{
|
||||
if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false)
|
||||
{
|
||||
throw new ArgumentException("the root element must be \"Files\"", "filesElement");
|
||||
}
|
||||
|
||||
return filesElement.Elements(Constants.Packaging.FileNodeName)
|
||||
.Select(e =>
|
||||
{
|
||||
XElement guidElement = e.Element(Constants.Packaging.GuidNodeName);
|
||||
if (guidElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName);
|
||||
if (orgPathElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName);
|
||||
if (orgNameElement == null)
|
||||
{
|
||||
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"",
|
||||
"filesElement");
|
||||
}
|
||||
|
||||
var fileName = PrepareAsFilePathElement(orgNameElement.Value);
|
||||
var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(orgPathElement.Value));
|
||||
|
||||
var relativePath = Path.Combine(relativeDir, fileName);
|
||||
|
||||
|
||||
return new KeyValuePair<string, string>(guidElement.Value, relativePath);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private static string PrepareAsFilePathElement(string pathElement)
|
||||
{
|
||||
return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\");
|
||||
}
|
||||
|
||||
private MetaData GetMetaData(XElement xRootElement)
|
||||
{
|
||||
XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName);
|
||||
|
||||
if (infoElement == null)
|
||||
{
|
||||
throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element",
|
||||
"xRootElement");
|
||||
}
|
||||
|
||||
var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath);
|
||||
var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath);
|
||||
var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath);
|
||||
var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath);
|
||||
var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath);
|
||||
var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath);
|
||||
var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath);
|
||||
var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath);
|
||||
var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath);
|
||||
var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath);
|
||||
|
||||
XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName);
|
||||
|
||||
return new MetaData
|
||||
{
|
||||
Name = StringValue(nameElement),
|
||||
Version = StringValue(versionElement),
|
||||
Url = StringValue(urlElement),
|
||||
License = StringValue(licenseElement),
|
||||
LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute),
|
||||
AuthorName = StringValue(authorNameElement),
|
||||
AuthorUrl = StringValue(authorUrlElement),
|
||||
Readme = StringValue(readmeElement),
|
||||
Control = StringValue(controlElement),
|
||||
ReqMajor = IntValue(majorElement),
|
||||
ReqMinor = IntValue(minorElement),
|
||||
ReqPatch = IntValue(patchElement)
|
||||
};
|
||||
}
|
||||
|
||||
private static string StringValue(XElement xElement, string defaultValue = "")
|
||||
{
|
||||
return xElement == null ? defaultValue : xElement.Value;
|
||||
}
|
||||
|
||||
private static string StringAttribute(XElement xElement, string attribute, string defaultValue = "")
|
||||
{
|
||||
return xElement == null
|
||||
? defaultValue
|
||||
: xElement.HasAttributes ? xElement.AttributeValue<string>(attribute) : defaultValue;
|
||||
}
|
||||
|
||||
private static int IntValue(XElement xElement, int defaultValue = 0)
|
||||
{
|
||||
int val;
|
||||
return xElement == null ? defaultValue : int.TryParse(xElement.Value, out val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
private static string UpdatePathPlaceholders(string path)
|
||||
{
|
||||
if (path.Contains("[$"))
|
||||
{
|
||||
//this is experimental and undocumented...
|
||||
path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco);
|
||||
path = path.Replace("[$UMBRACOCLIENT]", SystemDirectories.UmbracoClient);
|
||||
path = path.Replace("[$CONFIG]", SystemDirectories.Config);
|
||||
path = path.Replace("[$DATA]", SystemDirectories.Data);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal interface IPackageInstallation
|
||||
{
|
||||
InstallationSummary InstallPackage(string packageFilePath, int userId = 0);
|
||||
MetaData GetPackageMetaData(string packageFilePath);
|
||||
}
|
||||
|
||||
internal class PackageInstallation : IPackageInstallation
|
||||
{
|
||||
private readonly PackagingService _packagingService;
|
||||
private readonly PackageExtraction _packageExtraction;
|
||||
|
||||
public PackageInstallation(PackagingService packagingService, PackageExtraction packageExtraction)
|
||||
{
|
||||
_packagingService = packagingService;
|
||||
_packageExtraction = packageExtraction;
|
||||
}
|
||||
|
||||
public InstallationSummary InstallPackage(string packageFilePath, int userId = 0)
|
||||
{
|
||||
var summary = new InstallationSummary();
|
||||
return summary;
|
||||
}
|
||||
|
||||
public MetaData GetPackageMetaData(string packageFilePath)
|
||||
{
|
||||
var metaData = new MetaData();
|
||||
return metaData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,7 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
public static RuntimeCacheProvider Current { get { return lazy.Value; } }
|
||||
|
||||
//internal for testing! - though I'm not a huge fan of these being singletons!
|
||||
internal RuntimeCacheProvider()
|
||||
private RuntimeCacheProvider()
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
{
|
||||
@@ -247,7 +246,7 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
private string GetCompositeId(Type type, Guid id)
|
||||
{
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id);
|
||||
return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToString());
|
||||
}
|
||||
|
||||
private string GetCompositeId(Type type, int id)
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
};
|
||||
|
||||
if (entity.HasIdentity)
|
||||
dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return dto;
|
||||
}
|
||||
@@ -69,4 +69,4 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
ComputerName = entity.ComputerName
|
||||
};
|
||||
if (entity.HasIdentity)
|
||||
dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
public ITag BuildEntity(TagDto dto)
|
||||
{
|
||||
var model = new Tag(dto.Id, dto.Tag, dto.Group, dto.NodeCount);
|
||||
var model = new Tag(dto.Id, dto.Tag, dto.Group);
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
model.ResetDirtyProperties(false);
|
||||
@@ -20,8 +20,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
Id = entity.Id,
|
||||
Group = entity.Group,
|
||||
Tag = entity.Text,
|
||||
NodeCount = entity.NodeCount,
|
||||
Tag = entity.Text
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,10 +132,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
private void CreateCmsContentTypeData()
|
||||
{
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "icon-folder", IsContainer = false, AllowAtRoot = true });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "icon-picture" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "icon-document" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "icon-user" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = "icon-picture", Thumbnail = "mediaPhoto.png" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = "icon-document", Thumbnail = "mediaFile.png" });
|
||||
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = "icon-user", Thumbnail = "folder.png" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoUserData()
|
||||
@@ -255,7 +255,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
private void CreateUmbracoRelationTypeData()
|
||||
{
|
||||
_database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName });
|
||||
_database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = "relateDocumentOnCopy", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), Dual = true, Name = "Relate Document On Copy" });
|
||||
}
|
||||
|
||||
private void CreateCmsTaskTypeData()
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
/// <param name="database">The PetaPoco Database, which the migrations will be run against</param>
|
||||
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
|
||||
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
|
||||
public virtual bool Execute(Database database, bool isUpgrade = true)
|
||||
public bool Execute(Database database, bool isUpgrade = true)
|
||||
{
|
||||
return Execute(database, database.GetDatabaseProvider(), isUpgrade);
|
||||
}
|
||||
@@ -42,11 +42,11 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
/// <param name="databaseProvider"></param>
|
||||
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
|
||||
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
|
||||
public virtual bool Execute(Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
|
||||
public bool Execute(Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
|
||||
{
|
||||
LogHelper.Info<MigrationRunner>("Initializing database migrations");
|
||||
|
||||
var foundMigrations = FindMigrations();
|
||||
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
|
||||
|
||||
//filter all non-schema migrations
|
||||
var migrations = isUpgrade
|
||||
@@ -54,7 +54,6 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
: OrderedDowngradeMigrations(foundMigrations).ToList();
|
||||
|
||||
//SD: Why do we want this?
|
||||
//MCH: Because extensibility ... Mostly relevant to package developers who needs to utilize this type of event to add or remove migrations from the list
|
||||
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
|
||||
return false;
|
||||
|
||||
@@ -70,6 +69,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
//if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case,
|
||||
//since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
|
||||
//so in that case we have to downgrade
|
||||
|
||||
if (databaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
throw new DataLossException(
|
||||
@@ -86,57 +86,28 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
|
||||
/// </summary>
|
||||
/// <param name="foundMigrations"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
|
||||
private void ExecuteMigrations(IMigrationContext context, Database database)
|
||||
{
|
||||
var migrations = (from migration in foundMigrations
|
||||
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
|
||||
from migrationAttribute in migrationAttributes
|
||||
where migrationAttribute != null
|
||||
where migrationAttribute.TargetVersion > _currentVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName &&
|
||||
//filter if the migration specifies a minimum current version for which to execute
|
||||
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
|
||||
select migration).Distinct();
|
||||
return migrations;
|
||||
}
|
||||
//Transactional execution of the sql that was generated from the found migrations
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
int i = 1;
|
||||
foreach (var expression in context.Expressions)
|
||||
{
|
||||
var sql = expression.Process(database);
|
||||
if (string.IsNullOrEmpty(sql))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
|
||||
/// </summary>
|
||||
/// <param name="foundMigrations"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
|
||||
{
|
||||
var migrations = (from migration in foundMigrations
|
||||
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
|
||||
from migrationAttribute in migrationAttributes
|
||||
where migrationAttribute != null
|
||||
where
|
||||
migrationAttribute.TargetVersion > _currentVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName &&
|
||||
//filter if the migration specifies a minimum current version for which to execute
|
||||
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
|
||||
select migration).Distinct();
|
||||
return migrations;
|
||||
}
|
||||
LogHelper.Info<MigrationRunner>("Executing sql statement " + i + ": " + sql);
|
||||
database.Execute(sql);
|
||||
i++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all migrations that are available through the <see cref="MigrationResolver"/>
|
||||
/// </summary>
|
||||
/// <returns>An array of <see cref="IMigration"/></returns>
|
||||
protected virtual IMigration[] FindMigrations()
|
||||
{
|
||||
//MCH NOTE: Consider adding the ProductName filter to the Resolver so we don't get a bunch of irrelevant migrations
|
||||
return MigrationResolver.Current.Migrations.ToArray();
|
||||
transaction.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
internal MigrationContext InitializeMigrations(List<IMigration> migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
|
||||
@@ -179,28 +150,48 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
return context;
|
||||
}
|
||||
|
||||
private void ExecuteMigrations(IMigrationContext context, Database database)
|
||||
/// <summary>
|
||||
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
|
||||
/// </summary>
|
||||
/// <param name="foundMigrations"></param>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
|
||||
{
|
||||
//Transactional execution of the sql that was generated from the found migrations
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
int i = 1;
|
||||
foreach (var expression in context.Expressions)
|
||||
{
|
||||
var sql = expression.Process(database);
|
||||
if (string.IsNullOrEmpty(sql))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
var migrations = (from migration in foundMigrations
|
||||
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
|
||||
from migrationAttribute in migrationAttributes
|
||||
where migrationAttribute != null
|
||||
where
|
||||
migrationAttribute.TargetVersion > _currentVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName &&
|
||||
//filter if the migration specifies a minimum current version for which to execute
|
||||
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
|
||||
select migration).Distinct();
|
||||
return migrations;
|
||||
}
|
||||
|
||||
LogHelper.Info<MigrationRunner>("Executing sql statement " + i + ": " + sql);
|
||||
database.Execute(sql);
|
||||
i++;
|
||||
}
|
||||
|
||||
transaction.Complete();
|
||||
}
|
||||
/// <summary>
|
||||
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
|
||||
/// </summary>
|
||||
/// <param name="foundMigrations"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
|
||||
{
|
||||
var migrations = (from migration in foundMigrations
|
||||
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
|
||||
from migrationAttribute in migrationAttributes
|
||||
where migrationAttribute != null
|
||||
where
|
||||
migrationAttribute.TargetVersion > _currentVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName &&
|
||||
//filter if the migration specifies a minimum current version for which to execute
|
||||
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
|
||||
select migration).Distinct();
|
||||
return migrations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -221,7 +221,6 @@ namespace Umbraco.Core.Persistence
|
||||
if (overwrite && tableExist)
|
||||
{
|
||||
db.DropTable(tableName);
|
||||
tableExist = false;
|
||||
}
|
||||
|
||||
if (tableExist == false)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -126,10 +125,5 @@ namespace Umbraco.Core.Persistence
|
||||
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(rightColumnName));
|
||||
return sql.On(onClause);
|
||||
}
|
||||
|
||||
public static Sql OrderByDescending(this Sql sql, params object[] columns)
|
||||
{
|
||||
return sql.Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x.ToString() + " DESC").ToArray())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -257,9 +255,6 @@ 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);
|
||||
@@ -301,9 +296,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var userPermissions = (
|
||||
from perm in parentPermissions
|
||||
from p in perm.AssignedPermissions
|
||||
select new EntityPermissionSet.UserPermission(perm.UserId, p)).ToList();
|
||||
|
||||
permissionsRepo.ReplaceEntityPermissions(new EntityPermissionSet(entity.Id, userPermissions));
|
||||
select new Tuple<int, string>(perm.UserId, p)).ToList();
|
||||
|
||||
permissionsRepo.ReplaceEntityPermissions(entity, userPermissions);
|
||||
//flag the entity's permissions changed flag so we can track those changes.
|
||||
//Currently only used for the cache refreshers to detect if we should refresh all user permissions cache.
|
||||
((Content) entity).PermissionsChanged = true;
|
||||
@@ -371,9 +366,6 @@ 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"))
|
||||
{
|
||||
@@ -410,7 +402,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
//a flag that we'll use later to create the tags in the tag db table
|
||||
var publishedStateChanged = false;
|
||||
var isNewPublishedVersion = false;
|
||||
|
||||
//If Published state has changed then previous versions should have their publish state reset.
|
||||
//If state has been changed to unpublished the previous versions publish state should also be reset.
|
||||
@@ -426,7 +418,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
//this is a newly published version so we'll update the tags table too (end of this method)
|
||||
publishedStateChanged = true;
|
||||
isNewPublishedVersion = true;
|
||||
}
|
||||
|
||||
//Look up (newest) entries by id in cmsDocument table to set newest = false
|
||||
@@ -489,15 +481,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
//lastly, check if we are a newly published version and then update the tags table
|
||||
if (publishedStateChanged && entity.Published)
|
||||
if (isNewPublishedVersion)
|
||||
{
|
||||
UpdatePropertyTags(entity, _tagRepository);
|
||||
}
|
||||
else if (publishedStateChanged && (entity.Trashed || entity.Published == false))
|
||||
{
|
||||
//it's in the trash or not published remove all entity tags
|
||||
ClearEntityTags(entity, _tagRepository);
|
||||
}
|
||||
|
||||
((ICanBeDirty)entity).ResetDirtyProperties();
|
||||
}
|
||||
@@ -511,7 +498,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias && property.Value != null &&
|
||||
string.IsNullOrEmpty(property.Value.ToString()) == false
|
||||
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
|
||||
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
@@ -571,12 +558,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
|
||||
{
|
||||
var repo = new PermissionRepository<IContent>(UnitOfWork, _cacheHelper);
|
||||
repo.ReplaceEntityPermissions(permissionSet);
|
||||
}
|
||||
|
||||
public IContent GetByLanguage(int id, string language)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
@@ -644,117 +625,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(previewExists, content, xml));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged content results
|
||||
/// </summary>
|
||||
/// <param name="query">Query to excute</param>
|
||||
/// <param name="pageNumber">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records query would return without paging</param>
|
||||
/// <param name="orderBy">Field to order by</param>
|
||||
/// <param name="orderDirection">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, int pageNumber, int pageSize, out int totalRecords,
|
||||
string orderBy, Direction orderDirection, string filter = "")
|
||||
{
|
||||
// Get base query
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IContent>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.Where<DocumentDto>(x => x.Newest);
|
||||
|
||||
// Apply filter
|
||||
if (!string.IsNullOrEmpty(filter))
|
||||
{
|
||||
sql = sql.Where("cmsDocument.text LIKE @0", "%" + filter + "%");
|
||||
}
|
||||
|
||||
// Apply order according to parameters
|
||||
if (!string.IsNullOrEmpty(orderBy))
|
||||
{
|
||||
var orderByParams = new[] { GetDatabaseFieldNameForOrderBy(orderBy) };
|
||||
if (orderDirection == Direction.Ascending)
|
||||
{
|
||||
sql = sql.OrderBy(orderByParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = sql.OrderByDescending(orderByParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery,
|
||||
// but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id.
|
||||
// So we'll modify the SQL.
|
||||
var modifiedSQL = sql.SQL.Replace("SELECT *", "SELECT cmsDocument.nodeId");
|
||||
|
||||
// Get page of results and total count
|
||||
IEnumerable<IContent> result;
|
||||
var pagedResult = Database.Page<DocumentDto>(pageNumber, pageSize, modifiedSQL, sql.Arguments);
|
||||
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
||||
if (totalRecords > 0)
|
||||
{
|
||||
// Parse out node Ids and load content (we need the cast here in order to be able to call the IQueryable extension
|
||||
// methods OrderBy or OrderByDescending)
|
||||
var content = GetAll(pagedResult.Items
|
||||
.DistinctBy(x => x.NodeId)
|
||||
.Select(x => x.NodeId).ToArray())
|
||||
.Cast<Content>()
|
||||
.AsQueryable();
|
||||
|
||||
// Now we need to ensure this result is also ordered by the same order by clause
|
||||
var orderByProperty = GetIContentPropertyNameForOrderBy(orderBy);
|
||||
if (orderDirection == Direction.Ascending)
|
||||
{
|
||||
result = content.OrderBy(orderByProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = content.OrderByDescending(orderByProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Enumerable.Empty<IContent>();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string GetDatabaseFieldNameForOrderBy(string orderBy)
|
||||
{
|
||||
// Translate the passed order by field (which were originally defined for in-memory object sorting
|
||||
// of ContentItemBasic instances) to the database field names.
|
||||
switch (orderBy)
|
||||
{
|
||||
case "Name":
|
||||
return "cmsDocument.text";
|
||||
case "Owner":
|
||||
return "umbracoNode.nodeUser";
|
||||
case "Updator":
|
||||
return "cmsDocument.documentUser";
|
||||
default:
|
||||
return orderBy;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetIContentPropertyNameForOrderBy(string orderBy)
|
||||
{
|
||||
// Translate the passed order by field (which were originally defined for in-memory object sorting
|
||||
// of ContentItemBasic instances) to the IContent property names.
|
||||
switch (orderBy)
|
||||
{
|
||||
case "Owner":
|
||||
return "CreatorId";
|
||||
case "Updator":
|
||||
return "WriterId";
|
||||
default:
|
||||
return orderBy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -240,7 +240,7 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
var nodeId = contentDto.NodeId;
|
||||
var propertyTypeId = propertyType.Id;
|
||||
var propertySql = new Sql().Select("*")
|
||||
var propertySql = new Sql().Select("cmsPropertyData.id")
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<PropertyTypeDto>()
|
||||
.On<PropertyDataDto, PropertyTypeDto>(
|
||||
@@ -249,7 +249,7 @@ AND umbracoNode.id <> @id",
|
||||
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
|
||||
|
||||
//Finally delete the properties that match our criteria for removing a ContentType from the composition
|
||||
Database.Delete<PropertyDataDto>(propertySql);
|
||||
Database.Delete<PropertyDataDto>(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,8 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
}
|
||||
|
||||
if (((ICanBeDirty) entity).IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("PropertyGroups") ||
|
||||
entity.PropertyGroups.Any(x => x.IsDirty()))
|
||||
{
|
||||
//Delete Tabs/Groups by excepting entries from db with entries from collections
|
||||
var dbPropertyGroups =
|
||||
@@ -346,6 +347,25 @@ AND umbracoNode.id <> @id",
|
||||
if (propertyType.HasIdentity == false)
|
||||
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
|
||||
}
|
||||
|
||||
//If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType
|
||||
if (((ICanBeDirty)entity).IsPropertyDirty("ContentTypeComposition") &&
|
||||
compositionBase != null &&
|
||||
compositionBase.RemovedContentTypeKeyTracker != null &&
|
||||
compositionBase.RemovedContentTypeKeyTracker.Any())
|
||||
{
|
||||
foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker)
|
||||
{
|
||||
var dbPropertyGroups =
|
||||
Database.Fetch<PropertyTypeGroupDto>("WHERE contenttypeNodeId = @Id", new { Id = compositionId })
|
||||
.Select(x => x.Id);
|
||||
foreach (var propertyGroup in dbPropertyGroups)
|
||||
{
|
||||
Database.Update<PropertyTypeGroupDto>("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId",
|
||||
new { TabId = propertyGroup, ContentTypeNodeId = entity.Id });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)
|
||||
|
||||
@@ -426,7 +426,7 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
//go get the data
|
||||
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = dataTypeId });
|
||||
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
|
||||
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList();
|
||||
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
|
||||
|
||||
//now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method
|
||||
|
||||
@@ -146,9 +146,6 @@ 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);
|
||||
|
||||
@@ -170,9 +167,6 @@ 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);
|
||||
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentRepository : IRepositoryVersionable<int, IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
/// assigned to an entity with a list of user id & permission pairs.
|
||||
/// </summary>
|
||||
/// <param name="permissionSet"></param>
|
||||
void ReplaceContentPermissions(EntityPermissionSet permissionSet);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific language version of an <see cref="IContent"/>
|
||||
/// </summary>
|
||||
@@ -68,18 +59,5 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml);
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged content results
|
||||
/// </summary>
|
||||
/// <param name="query">Query to excute</param>
|
||||
/// <param name="pageNumber">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records query would return without paging</param>
|
||||
/// <param name="orderBy">Field to order by</param>
|
||||
/// <param name="orderDirection">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, int pageNumber, int pageSize, out int totalRecords,
|
||||
string orderBy, Direction orderDirection, string filter = "");
|
||||
}
|
||||
}
|
||||
@@ -12,17 +12,6 @@ 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>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Returns all tags for an entity type (content/media/member)
|
||||
/// </summary>
|
||||
/// <param name="objectType">Entity type</param>
|
||||
/// <param name="objectType"></param>
|
||||
/// <param name="group">Optional group</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<ITag> GetTagsForEntityType(TaggableObjectTypes objectType, string group = null);
|
||||
@@ -57,18 +57,5 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="propertyTypeId"></param>
|
||||
/// <param name="tags">The tags to remove from the property</param>
|
||||
void RemoveTagsFromProperty(int contentId, int propertyTypeId, IEnumerable<ITag> tags);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all tag associations from the property
|
||||
/// </summary>
|
||||
/// <param name="contentId"></param>
|
||||
/// <param name="propertyTypeId"></param>
|
||||
void ClearTagsFromProperty(int contentId, int propertyTypeId);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all tag associations from the entity
|
||||
/// </summary>
|
||||
/// <param name="contentId"></param>
|
||||
void ClearTagsFromEntity(int contentId);
|
||||
}
|
||||
}
|
||||
@@ -188,22 +188,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
else
|
||||
{
|
||||
//This will only work if the Alias hasn't been changed
|
||||
if (macro.Properties.ContainsKey(propDto.Alias))
|
||||
//only update if it's dirty
|
||||
if (macro.Properties[propDto.Alias].IsDirty())
|
||||
{
|
||||
//only update if it's dirty
|
||||
if (macro.Properties[propDto.Alias].IsDirty())
|
||||
{
|
||||
Database.Update(propDto);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var property = macro.Properties.FirstOrDefault(x => x.Id == propDto.Id);
|
||||
if (property != null && property.IsDirty())
|
||||
{
|
||||
Database.Update(propDto);
|
||||
}
|
||||
Database.Update(propDto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,9 +220,6 @@ 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);
|
||||
|
||||
@@ -292,9 +289,6 @@ 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"))
|
||||
{
|
||||
@@ -375,7 +369,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias &&
|
||||
string.IsNullOrEmpty(property.Value.ToString()) == false
|
||||
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
|
||||
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
|
||||
@@ -209,9 +209,6 @@ 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);
|
||||
|
||||
@@ -287,9 +284,6 @@ 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
|
||||
@@ -404,7 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
if (property.PropertyType.PropertyEditorAlias == uploadFieldAlias &&
|
||||
string.IsNullOrEmpty(property.Value.ToString()) == false
|
||||
&& fs.FileExists(fs.GetRelativePath(property.Value.ToString())))
|
||||
&& fs.FileExists(IOHelper.MapPath(property.Value.ToString())))
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(property.Value.ToString());
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
|
||||
@@ -191,23 +191,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Assigns permissions to an entity for multiple users/permission entries
|
||||
/// </summary>
|
||||
/// <param name="permissionSet">
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="userPermissions">
|
||||
/// A key/value pair list containing a userId and a permission to assign
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This will first clear the permissions for this entity then re-create them
|
||||
/// </remarks>
|
||||
public void ReplaceEntityPermissions(EntityPermissionSet permissionSet)
|
||||
public void ReplaceEntityPermissions(TEntity entity, IEnumerable<Tuple<int, string>> userPermissions)
|
||||
{
|
||||
var db = _unitOfWork.Database;
|
||||
using (var trans = db.GetTransaction())
|
||||
{
|
||||
db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = permissionSet.EntityId });
|
||||
db.Execute("DELETE FROM umbracoUser2NodePermission WHERE nodeId=@nodeId", new { nodeId = entity.Id });
|
||||
|
||||
var actions = permissionSet.UserPermissionsSet.Select(p => new User2NodePermissionDto
|
||||
var actions = userPermissions.Select(p => new User2NodePermissionDto
|
||||
{
|
||||
NodeId = permissionSet.EntityId,
|
||||
Permission = p.Permission,
|
||||
UserId = p.UserId
|
||||
NodeId = entity.Id,
|
||||
Permission = p.Item2,
|
||||
UserId = p.Item1
|
||||
}).ToArray();
|
||||
|
||||
_unitOfWork.Database.BulkInsertRecords(actions, trans);
|
||||
|
||||
@@ -133,9 +133,6 @@ 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,9 +64,6 @@ 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)
|
||||
|
||||
@@ -106,9 +106,6 @@ 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)
|
||||
|
||||
@@ -230,96 +230,63 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var nodeObjectType = GetNodeObjectType(objectType);
|
||||
|
||||
var sql = GetTagsQuerySelect(true);
|
||||
|
||||
sql = ApplyRelationshipJoinToTagsQuery(sql);
|
||||
|
||||
sql = sql
|
||||
var sql = new Sql()
|
||||
.Select("DISTINCT cmsTags.*")
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>()
|
||||
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
|
||||
.InnerJoin<ContentDto>()
|
||||
.On<ContentDto, TagRelationshipDto>(left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>()
|
||||
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeObjectType == nodeObjectType);
|
||||
|
||||
sql = ApplyGroupFilterToTagsQuery(sql, group);
|
||||
if (group.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
sql = sql.Where<TagDto>(dto => dto.Group == group);
|
||||
}
|
||||
|
||||
sql = ApplyGroupByToTagsQuery(sql);
|
||||
var factory = new TagFactory();
|
||||
|
||||
return ExecuteTagsQuery(sql);
|
||||
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
|
||||
}
|
||||
|
||||
public IEnumerable<ITag> GetTagsForEntity(int contentId, string group = null)
|
||||
{
|
||||
var sql = GetTagsQuerySelect();
|
||||
|
||||
sql = ApplyRelationshipJoinToTagsQuery(sql);
|
||||
|
||||
sql = sql
|
||||
var sql = new Sql()
|
||||
.Select("DISTINCT cmsTags.*")
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>()
|
||||
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
|
||||
.Where<TagRelationshipDto>(dto => dto.NodeId == contentId);
|
||||
|
||||
sql = ApplyGroupFilterToTagsQuery(sql, group);
|
||||
if (group.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
sql = sql.Where<TagDto>(dto => dto.Group == group);
|
||||
}
|
||||
|
||||
return ExecuteTagsQuery(sql);
|
||||
var factory = new TagFactory();
|
||||
|
||||
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
|
||||
}
|
||||
|
||||
public IEnumerable<ITag> GetTagsForProperty(int contentId, string propertyTypeAlias, string group = null)
|
||||
{
|
||||
var sql = GetTagsQuerySelect();
|
||||
|
||||
sql = ApplyRelationshipJoinToTagsQuery(sql);
|
||||
|
||||
sql = sql
|
||||
var sql = new Sql()
|
||||
.Select("DISTINCT cmsTags.*")
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>()
|
||||
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id)
|
||||
.InnerJoin<PropertyTypeDto>()
|
||||
.On<PropertyTypeDto, TagRelationshipDto>(left => left.Id, right => right.PropertyTypeId)
|
||||
.Where<TagRelationshipDto>(dto => dto.NodeId == contentId)
|
||||
.Where<PropertyTypeDto>(dto => dto.Alias == propertyTypeAlias);
|
||||
|
||||
sql = ApplyGroupFilterToTagsQuery(sql, group);
|
||||
|
||||
return ExecuteTagsQuery(sql);
|
||||
}
|
||||
|
||||
private Sql GetTagsQuerySelect(bool withGrouping = false)
|
||||
{
|
||||
var sql = new Sql();
|
||||
|
||||
if (withGrouping)
|
||||
{
|
||||
sql = sql.Select("cmsTags.Id, cmsTags.Tag, cmsTags.[Group], Count(*) NodeCount");
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = sql.Select("DISTINCT cmsTags.*");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private Sql ApplyRelationshipJoinToTagsQuery(Sql sql)
|
||||
{
|
||||
return sql
|
||||
.From<TagDto>()
|
||||
.InnerJoin<TagRelationshipDto>()
|
||||
.On<TagRelationshipDto, TagDto>(left => left.TagId, right => right.Id);
|
||||
}
|
||||
|
||||
private Sql ApplyGroupFilterToTagsQuery(Sql sql, string group)
|
||||
{
|
||||
if (!group.IsNullOrWhiteSpace())
|
||||
if (group.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
sql = sql.Where<TagDto>(dto => dto.Group == group);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private Sql ApplyGroupByToTagsQuery(Sql sql)
|
||||
{
|
||||
return sql.GroupBy(new string[] { "cmsTags.Id", "cmsTags.Tag", "cmsTags.[Group]" });
|
||||
}
|
||||
|
||||
private IEnumerable<ITag> ExecuteTagsQuery(Sql sql)
|
||||
{
|
||||
var factory = new TagFactory();
|
||||
|
||||
return Database.Fetch<TagDto>(sql).Select(factory.BuildEntity);
|
||||
@@ -443,18 +410,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Database.Execute(deleteSql);
|
||||
}
|
||||
|
||||
public void ClearTagsFromProperty(int contentId, int propertyTypeId)
|
||||
{
|
||||
Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId AND propertyTypeId = @propertyTypeId",
|
||||
new { nodeId = contentId, propertyTypeId = propertyTypeId });
|
||||
}
|
||||
|
||||
public void ClearTagsFromEntity(int contentId)
|
||||
{
|
||||
Database.Execute("DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId",
|
||||
new { nodeId = contentId });
|
||||
}
|
||||
|
||||
private Guid GetNodeObjectType(TaggableObjectTypes type)
|
||||
{
|
||||
switch (type)
|
||||
|
||||
@@ -371,7 +371,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
//template.CreateDate = _viewsFileSystem.GetCreated(path).UtcDateTime;
|
||||
//template.Key = new FileInfo(path).Name.EncodeAsGuid();
|
||||
|
||||
template.Path = path;
|
||||
template.Content = content;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -96,100 +93,6 @@ 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 removes associated tags from the entity - used generally when an entity is recycled
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="tagRepo"></param>
|
||||
protected void ClearEntityTags(IContentBase entity, ITagsRepository tagRepo)
|
||||
{
|
||||
tagRepo.ClearTagsFromEntity(entity.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the tag repository with any tag enabled properties and their values
|
||||
/// </summary>
|
||||
@@ -232,33 +135,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentTypeComposition contentType, DateTime createDate, DateTime updateDate)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select("cmsPropertyData.*, cmsDataTypePreValues.id as preValId, cmsDataTypePreValues.value, cmsDataTypePreValues.sortorder, cmsDataTypePreValues.alias, cmsDataTypePreValues.datatypeNodeId")
|
||||
sql.Select("*")
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<PropertyTypeDto>()
|
||||
.On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
|
||||
|
||||
.LeftOuterJoin<DataTypePreValueDto>()
|
||||
.On<PropertyTypeDto, DataTypePreValueDto>(left => left.DataTypeId, right => right.DataTypeNodeId)
|
||||
|
||||
.Where<PropertyDataDto>(x => x.NodeId == id)
|
||||
.Where<PropertyDataDto>(x => x.VersionId == versionId);
|
||||
|
||||
var allData = Database.Fetch<dynamic>(sql);
|
||||
|
||||
var propertyDataDtos = allData.Select(x => new PropertyDataDto
|
||||
{
|
||||
Date = x.dataDate,
|
||||
Id = x.id,
|
||||
Integer = x.dataInt,
|
||||
NodeId = x.contentNodeId,
|
||||
Text = x.dataNtext,
|
||||
VarChar = x.dataNvarchar,
|
||||
VersionId = x.versionId,
|
||||
PropertyTypeId = x.propertytypeid,
|
||||
//NOTE: This get's used for nothing so we don't need to map it
|
||||
//PropertyTypeDto = new PropertyTypeDto()
|
||||
}).Distinct();
|
||||
|
||||
var propertyDataDtos = Database.Fetch<PropertyDataDto, PropertyTypeDto>(sql);
|
||||
var propertyFactory = new PropertyFactory(contentType, versionId, id, createDate, updateDate);
|
||||
var properties = propertyFactory.BuildEntity(propertyDataDtos).ToArray();
|
||||
|
||||
@@ -272,39 +156,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
property.Id = primaryKey;
|
||||
}
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias);
|
||||
//TODO: Should this be cached somehow? Might need to benchmark this
|
||||
var tagSupport = TagExtractor.GetAttribute(editor);
|
||||
|
||||
if (tagSupport != null)
|
||||
{
|
||||
|
||||
//this property has tags, so we need to extract them and for that we need the prevals which we've already looked up
|
||||
|
||||
var preValData = allData.Where(x => x.propertytypeid == property.PropertyTypeId && x.preValId != null)
|
||||
.Select(x => new DataTypePreValueDto
|
||||
{
|
||||
Alias = x.alias,
|
||||
DataTypeNodeId = x.datatypeNodeId,
|
||||
Id = x.preValId,
|
||||
SortOrder = x.sortorder,
|
||||
Value = x.value
|
||||
})
|
||||
.ToDictionary(x => x.Alias, x => new PreValue(x.Id, x.Value, x.SortOrder));
|
||||
|
||||
var preVals = new PreValueCollection(preValData);
|
||||
|
||||
var d = new ContentPropertyData(property.Value,
|
||||
preVals,
|
||||
new Dictionary<string, object>());
|
||||
|
||||
TagExtractor.SetPropertyTags(property, d, property.Value, tagSupport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new PropertyCollection(properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
throw new NotImplementedException("This method must be implemented if a view is not explicitly set");
|
||||
}
|
||||
|
||||
editor.View = _attribute.EditorView.StartsWith("~/") ? IOHelper.ResolveUrl(_attribute.EditorView) : _attribute.EditorView;
|
||||
editor.View = _attribute.EditorView;
|
||||
editor.ValueType = _attribute.ValueType;
|
||||
editor.HideLabel = _attribute.HideLabel;
|
||||
return editor;
|
||||
|
||||
@@ -223,12 +223,6 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </remarks>
|
||||
public virtual object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
|
||||
{
|
||||
//if it's json but it's empty json, then return null
|
||||
if (ValueType.InvariantEquals("JSON") && editorValue.Value != null && editorValue.Value.ToString().DetectIsEmptyJson())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = TryConvertValueToCrlType(editorValue.Value);
|
||||
if (result.Success == false)
|
||||
{
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
public class ColorPickerValueConverter : PropertyValueConverterBase
|
||||
{
|
||||
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
return propertyType.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.ColorPickerAlias);
|
||||
}
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// make sure it's a string
|
||||
return source.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,34 +16,12 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
// in xml a boolean is: string
|
||||
// in the database a boolean is: string "1" or "0" or empty
|
||||
// typically the converter does not need to handle anything else ("true"...)
|
||||
// however there are cases where the value passed to the converter could be a non-string object, e.g. int, bool
|
||||
|
||||
if (source is string)
|
||||
{
|
||||
var str = (string)source;
|
||||
|
||||
if (str == null || str.Length == 0 || str == "0")
|
||||
return false;
|
||||
|
||||
if (str == "1")
|
||||
return true;
|
||||
|
||||
bool result;
|
||||
if (bool.TryParse(str, out result))
|
||||
return result;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source is int)
|
||||
return (int)source == 1;
|
||||
|
||||
if (source is bool)
|
||||
return (bool)source;
|
||||
// the converter does not need to handle anything else ("true"...)
|
||||
|
||||
// default value is: false
|
||||
return false;
|
||||
var sourceString = source as string;
|
||||
if (sourceString == null) return false;
|
||||
return sourceString == "1";
|
||||
}
|
||||
|
||||
// default ConvertSourceToObject just returns source ie a boolean value
|
||||
@@ -51,7 +29,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// source should come from ConvertSource and be a boolean already
|
||||
return (bool)source ? "1" : "0";
|
||||
return (bool) source ? "1" : "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
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", 20, false, 0);
|
||||
_maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
|
||||
_passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
|
||||
_minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80);
|
||||
_minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", DefaultMinNonAlphanumericChars, true, 0x80);
|
||||
|
||||
@@ -62,30 +62,12 @@ namespace Umbraco.Core.Security
|
||||
return Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider];
|
||||
}
|
||||
|
||||
/// <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()
|
||||
? null
|
||||
: 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);
|
||||
: membershipProvider.GetUser(username, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,14 +6,15 @@ using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
@@ -73,47 +74,6 @@ 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>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
/// assigned to an entity with a list of user id & permission pairs.
|
||||
/// </summary>
|
||||
/// <param name="permissionSet"></param>
|
||||
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
repository.ReplaceContentPermissions(permissionSet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a single permission to the current content item for the specified user ids
|
||||
/// </summary>
|
||||
@@ -472,29 +432,6 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the Parent to retrieve Children from</param>
|
||||
/// <param name="pageNumber">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records query would return without paging</param>
|
||||
/// <param name="orderBy">Field to order by</param>
|
||||
/// <param name="orderDirections">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IContent> GetPagedChildren(int id, int pageNumber, int pageSize, out int totalChildren,
|
||||
string orderBy, Direction orderDirection, string filter = "")
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.ParentId == id);
|
||||
var contents = repository.GetPagedResultsByQuery(query, pageNumber, pageSize, out totalChildren, orderBy, orderDirection, filter);
|
||||
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by its name or partial name
|
||||
/// </summary>
|
||||
@@ -752,7 +689,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
var result = SaveAndPublishDo(content, userId);
|
||||
LogHelper.Info<ContentService>("Call was made to ContentService.Publish, use PublishWithStatus instead since that method will provide more detailed information on the outcome");
|
||||
return result.Success;
|
||||
return result.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -782,7 +719,7 @@ namespace Umbraco.Core.Services
|
||||
if (!result.Any(x => x.Result.ContentItem.Id == content.Id))
|
||||
return false;
|
||||
|
||||
return result.Single(x => x.Result.ContentItem.Id == content.Id).Success;
|
||||
return result.Single(x => x.Result.ContentItem.Id == content.Id).Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -819,7 +756,7 @@ namespace Umbraco.Core.Services
|
||||
public bool SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var result = SaveAndPublishDo(content, userId, raiseEvents);
|
||||
return result.Success;
|
||||
return result.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -883,7 +820,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
//add or update preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -894,7 +831,7 @@ namespace Umbraco.Core.Services
|
||||
repository.AddOrUpdate(content);
|
||||
//add or update preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
@@ -1135,7 +1072,7 @@ namespace Umbraco.Core.Services
|
||||
MoveToRecycleBin(content, userId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Moving.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IContent>(
|
||||
new MoveEventInfo<IContent>(content, content.Path, parentId)), this))
|
||||
@@ -1222,21 +1159,37 @@ namespace Umbraco.Core.Services
|
||||
repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
|
||||
|
||||
//Special case for the associated tags
|
||||
//TODO: Move this to the repository layer in a single transaction!
|
||||
//don't copy tags data in tags table if the item is in the recycle bin
|
||||
if (parentId != Constants.System.RecycleBinContent)
|
||||
//Special case for the associated tags
|
||||
var tags = uow.Database.Fetch<TagRelationshipDto>("WHERE nodeId = @Id", new { Id = content.Id });
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
|
||||
var tags = uow.Database.Fetch<TagRelationshipDto>("WHERE nodeId = @Id", new { Id = content.Id });
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
uow.Database.Insert(new TagRelationshipDto { NodeId = copy.Id, TagId = tag.TagId, PropertyTypeId = tag.PropertyTypeId });
|
||||
}
|
||||
uow.Database.Insert(new TagRelationshipDto { NodeId = copy.Id, TagId = tag.TagId, PropertyTypeId = tag.PropertyTypeId });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//NOTE This 'Relation' part should eventually be delegated to a RelationService
|
||||
//TODO: This should be part of a single commit
|
||||
if (relateToOriginal)
|
||||
{
|
||||
IRelationType relationType = null;
|
||||
using (var relationTypeRepository = _repositoryFactory.CreateRelationTypeRepository(uow))
|
||||
{
|
||||
relationType = relationTypeRepository.Get(1);
|
||||
}
|
||||
|
||||
using (var relationRepository = _repositoryFactory.CreateRelationRepository(uow))
|
||||
{
|
||||
var relation = new Relation(content.Id, copy.Id, relationType);
|
||||
relationRepository.AddOrUpdate(relation);
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Copy,
|
||||
string.Format("Copied content with Id: '{0}' related to original content with Id: '{1}'",
|
||||
copy.Id, content.Id), copy.WriterId, copy.Id);
|
||||
}
|
||||
|
||||
//Look for children and copy those as well
|
||||
var children = GetChildren(content.Id);
|
||||
foreach (var child in children)
|
||||
@@ -1246,10 +1199,10 @@ namespace Umbraco.Core.Services
|
||||
Copy(child, copy.Id, relateToOriginal, userId);
|
||||
}
|
||||
|
||||
Copied.RaiseEvent(new CopyEventArgs<IContent>(content, copy, false, parentId, relateToOriginal), this);
|
||||
Copied.RaiseEvent(new CopyEventArgs<IContent>(content, copy, false, parentId), this);
|
||||
|
||||
Audit.Add(AuditTypes.Copy, "Copy Content performed by user", content.WriterId, content.Id);
|
||||
|
||||
|
||||
//TODO: Don't think we need this here because cache should be cleared by the event listeners
|
||||
// and the correct ICacheRefreshers!?
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
@@ -1378,9 +1331,9 @@ namespace Umbraco.Core.Services
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
|
||||
|
||||
foreach (var content in shouldBePublished)
|
||||
{
|
||||
{
|
||||
//Create and Save ContentXml DTO
|
||||
repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
@@ -1571,7 +1524,7 @@ namespace Umbraco.Core.Services
|
||||
//bulk insert it into the database
|
||||
uow.Database.BulkInsertRecords(xmlItems, tr);
|
||||
|
||||
tr.Complete();
|
||||
tr.Complete();
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
|
||||
@@ -1676,26 +1629,29 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>True if unpublishing succeeded, otherwise False</returns>
|
||||
private bool UnPublishDo(IContent content, bool omitCacheRefresh = false, int userId = 0)
|
||||
{
|
||||
var unpublished = _publishingStrategy.UnPublish(content, userId);
|
||||
if (unpublished)
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
var unpublished = _publishingStrategy.UnPublish(content, userId);
|
||||
if (unpublished)
|
||||
{
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
repository.DeleteContentXml(content);
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
repository.DeleteContentXml(content);
|
||||
|
||||
uow.Commit();
|
||||
uow.Commit();
|
||||
}
|
||||
//Delete xml from db? and call following method to fire event through PublishingStrategy to update cache
|
||||
if (omitCacheRefresh == false)
|
||||
_publishingStrategy.UnPublishingFinalized(content);
|
||||
|
||||
Audit.Add(AuditTypes.UnPublish, "UnPublish performed by user", userId, content.Id);
|
||||
}
|
||||
//Delete xml from db? and call following method to fire event through PublishingStrategy to update cache
|
||||
if (omitCacheRefresh == false)
|
||||
_publishingStrategy.UnPublishingFinalized(content);
|
||||
|
||||
Audit.Add(AuditTypes.UnPublish, "UnPublish performed by user", userId, content.Id);
|
||||
return unpublished;
|
||||
}
|
||||
|
||||
return unpublished;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1758,7 +1714,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
//Generate a new preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
|
||||
|
||||
if (published)
|
||||
{
|
||||
//Content Xml
|
||||
@@ -1898,7 +1854,7 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
|
||||
return PublishStatusType.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private IContentType FindContentTypeByAlias(string contentTypeAlias)
|
||||
{
|
||||
|
||||
@@ -127,244 +127,6 @@ namespace Umbraco.Core.Services
|
||||
return xElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports an <see cref="IDataTypeDefinition"/> item to xml as an <see cref="XElement"/>
|
||||
/// </summary>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="dataTypeDefinition">IDataTypeDefinition type to export</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
|
||||
public XElement Serialize(IDataTypeService dataTypeService, IDataTypeDefinition dataTypeDefinition)
|
||||
{
|
||||
var prevalues = new XElement("PreValues");
|
||||
var prevalueList = dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeDefinition.Id)
|
||||
.FormatAsDictionary();
|
||||
|
||||
var sort = 0;
|
||||
foreach (var pv in prevalueList)
|
||||
{
|
||||
var prevalue = new XElement("PreValue");
|
||||
prevalue.Add(new XAttribute("Id", pv.Value.Id));
|
||||
prevalue.Add(new XAttribute("Value", pv.Value.Value ?? ""));
|
||||
prevalue.Add(new XAttribute("Alias", pv.Key));
|
||||
prevalue.Add(new XAttribute("SortOrder", sort));
|
||||
prevalues.Add(prevalue);
|
||||
sort++;
|
||||
}
|
||||
|
||||
var xml = new XElement("DataType", prevalues);
|
||||
xml.Add(new XAttribute("Name", dataTypeDefinition.Name));
|
||||
//The 'ID' when exporting is actually the property editor alias (in pre v7 it was the IDataType GUID id)
|
||||
xml.Add(new XAttribute("Id", dataTypeDefinition.PropertyEditorAlias));
|
||||
xml.Add(new XAttribute("Definition", dataTypeDefinition.Key));
|
||||
xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString()));
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(IDictionaryItem dictionaryItem)
|
||||
{
|
||||
var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey));
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
{
|
||||
xml.Add(new XElement("Value",
|
||||
new XAttribute("LanguageId", translation.Language.Id),
|
||||
new XAttribute("LanguageCultureAlias", translation.Language.IsoCode),
|
||||
new XCData(translation.Value)));
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(ILanguage language)
|
||||
{
|
||||
var xml = new XElement("Language",
|
||||
new XAttribute("Id", language.Id),
|
||||
new XAttribute("CultureAlias", language.IsoCode),
|
||||
new XAttribute("FriendlyName", language.CultureName));
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(ITemplate template)
|
||||
{
|
||||
var xml = new XElement("Template");
|
||||
xml.Add(new XElement("Name", template.Name));
|
||||
xml.Add(new XElement("Alias", template.Alias));
|
||||
xml.Add(new XElement("Design", new XCData(template.Content)));
|
||||
|
||||
var concreteTemplate = template as Template;
|
||||
if (concreteTemplate != null && concreteTemplate.MasterTemplateId != null)
|
||||
{
|
||||
if (concreteTemplate.MasterTemplateId.IsValueCreated &&
|
||||
concreteTemplate.MasterTemplateId.Value != default(int))
|
||||
{
|
||||
xml.Add(new XElement("Master", concreteTemplate.MasterTemplateId.ToString()));
|
||||
xml.Add(new XElement("MasterAlias", concreteTemplate.MasterTemplateAlias));
|
||||
}
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(IDataTypeService dataTypeService, IMediaType mediaType)
|
||||
{
|
||||
var info = new XElement("Info",
|
||||
new XElement("Name", mediaType.Name),
|
||||
new XElement("Alias", mediaType.Alias),
|
||||
new XElement("Icon", mediaType.Icon),
|
||||
new XElement("Thumbnail", mediaType.Thumbnail),
|
||||
new XElement("Description", mediaType.Description),
|
||||
new XElement("AllowAtRoot", mediaType.AllowedAsRoot.ToString()));
|
||||
|
||||
var masterContentType = mediaType.CompositionAliases().FirstOrDefault();
|
||||
if (masterContentType != null)
|
||||
info.Add(new XElement("Master", masterContentType));
|
||||
|
||||
var structure = new XElement("Structure");
|
||||
foreach (var allowedType in mediaType.AllowedContentTypes)
|
||||
{
|
||||
structure.Add(new XElement("MediaType", allowedType.Alias));
|
||||
}
|
||||
|
||||
var genericProperties = new XElement("GenericProperties");
|
||||
foreach (var propertyType in mediaType.PropertyTypes)
|
||||
{
|
||||
var definition = dataTypeService.GetDataTypeDefinitionById(propertyType.DataTypeDefinitionId);
|
||||
var propertyGroup = mediaType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value);
|
||||
var genericProperty = new XElement("GenericProperty",
|
||||
new XElement("Name", propertyType.Name),
|
||||
new XElement("Alias", propertyType.Alias),
|
||||
new XElement("Type", propertyType.PropertyEditorAlias),
|
||||
new XElement("Definition", definition.Key),
|
||||
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
|
||||
new XElement("Mandatory", propertyType.Mandatory.ToString()),
|
||||
new XElement("Validation", propertyType.ValidationRegExp),
|
||||
new XElement("Description", new XCData(propertyType.Description)));
|
||||
genericProperties.Add(genericProperty);
|
||||
}
|
||||
|
||||
var tabs = new XElement("Tabs");
|
||||
foreach (var propertyGroup in mediaType.PropertyGroups)
|
||||
{
|
||||
var tab = new XElement("Tab",
|
||||
new XElement("Id", propertyGroup.Id.ToString(CultureInfo.InvariantCulture)),
|
||||
new XElement("Caption", propertyGroup.Name));
|
||||
tabs.Add(tab);
|
||||
}
|
||||
|
||||
var xml = new XElement("MediaType",
|
||||
info,
|
||||
structure,
|
||||
genericProperties,
|
||||
tabs);
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(IMacro macro)
|
||||
{
|
||||
var xml = new XElement("macro");
|
||||
xml.Add(new XElement("name", macro.Name));
|
||||
xml.Add(new XElement("alias", macro.Alias));
|
||||
xml.Add(new XElement("scriptType", macro.ControlType));
|
||||
xml.Add(new XElement("scriptAssembly", macro.ControlAssembly));
|
||||
xml.Add(new XElement("scriptingFile", macro.ScriptPath));
|
||||
xml.Add(new XElement("xslt", macro.XsltPath));
|
||||
xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString()));
|
||||
xml.Add(new XElement("dontRender", macro.DontRender.ToString()));
|
||||
xml.Add(new XElement("refreshRate", macro.CacheDuration.ToString(CultureInfo.InvariantCulture)));
|
||||
xml.Add(new XElement("cacheByMember", macro.CacheByMember.ToString()));
|
||||
xml.Add(new XElement("cacheByPage", macro.CacheByPage.ToString()));
|
||||
|
||||
var properties = new XElement("properties");
|
||||
foreach (var property in macro.Properties)
|
||||
{
|
||||
properties.Add(new XElement("property",
|
||||
new XAttribute("name", property.Name),
|
||||
new XAttribute("alias", property.Alias),
|
||||
new XAttribute("sortOrder", property.SortOrder),
|
||||
new XAttribute("propertyType", property.EditorAlias)));
|
||||
}
|
||||
xml.Add(properties);
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports an <see cref="IContentType"/> item to xml as an <see cref="XElement"/>
|
||||
/// </summary>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="contentType">Content type to export</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the IContentType object</returns>
|
||||
public XElement Serialize(IDataTypeService dataTypeService, IContentType contentType)
|
||||
{
|
||||
var info = new XElement("Info",
|
||||
new XElement("Name", contentType.Name),
|
||||
new XElement("Alias", contentType.Alias),
|
||||
new XElement("Icon", contentType.Icon),
|
||||
new XElement("Thumbnail", contentType.Thumbnail),
|
||||
new XElement("Description", contentType.Description),
|
||||
new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()),
|
||||
new XElement("IsListView", contentType.IsContainer.ToString()));
|
||||
|
||||
var masterContentType = contentType.CompositionAliases().FirstOrDefault();
|
||||
if (masterContentType != null)
|
||||
info.Add(new XElement("Master", masterContentType));
|
||||
|
||||
var allowedTemplates = new XElement("AllowedTemplates");
|
||||
foreach (var template in contentType.AllowedTemplates)
|
||||
{
|
||||
allowedTemplates.Add(new XElement("Template", template.Alias));
|
||||
}
|
||||
info.Add(allowedTemplates);
|
||||
if (contentType.DefaultTemplate != null && contentType.DefaultTemplate.Id != 0)
|
||||
info.Add(new XElement("DefaultTemplate", contentType.DefaultTemplate.Alias));
|
||||
else
|
||||
info.Add(new XElement("DefaultTemplate", ""));
|
||||
|
||||
var structure = new XElement("Structure");
|
||||
foreach (var allowedType in contentType.AllowedContentTypes)
|
||||
{
|
||||
structure.Add(new XElement("DocumentType", allowedType.Alias));
|
||||
}
|
||||
|
||||
var genericProperties = new XElement("GenericProperties");
|
||||
foreach (var propertyType in contentType.PropertyTypes)
|
||||
{
|
||||
var definition = dataTypeService.GetDataTypeDefinitionById(propertyType.DataTypeDefinitionId);
|
||||
|
||||
var propertyGroup = propertyType.PropertyGroupId == null
|
||||
? null
|
||||
: contentType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value);
|
||||
|
||||
var genericProperty = new XElement("GenericProperty",
|
||||
new XElement("Name", propertyType.Name),
|
||||
new XElement("Alias", propertyType.Alias),
|
||||
new XElement("Type", propertyType.PropertyEditorAlias),
|
||||
new XElement("Definition", definition.Key),
|
||||
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
|
||||
new XElement("Mandatory", propertyType.Mandatory.ToString()),
|
||||
new XElement("Validation", propertyType.ValidationRegExp),
|
||||
new XElement("Description", new XCData(propertyType.Description)));
|
||||
genericProperties.Add(genericProperty);
|
||||
}
|
||||
|
||||
var tabs = new XElement("Tabs");
|
||||
foreach (var propertyGroup in contentType.PropertyGroups)
|
||||
{
|
||||
var tab = new XElement("Tab",
|
||||
new XElement("Id", propertyGroup.Id.ToString(CultureInfo.InvariantCulture)),
|
||||
new XElement("Caption", propertyGroup.Name));
|
||||
tabs.Add(tab);
|
||||
}
|
||||
|
||||
return new XElement("DocumentType",
|
||||
info,
|
||||
structure,
|
||||
genericProperties,
|
||||
tabs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Media Export to recursively add children
|
||||
/// </summary>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
@@ -23,21 +17,19 @@ namespace Umbraco.Core.Services
|
||||
private readonly RepositoryFactory _repositoryFactory;
|
||||
private readonly IUnitOfWorkProvider _fileUowProvider;
|
||||
private readonly IDatabaseUnitOfWorkProvider _dataUowProvider;
|
||||
private readonly IMacroService _macroService;
|
||||
|
||||
public FileService()
|
||||
: this(new RepositoryFactory())
|
||||
{ }
|
||||
|
||||
public FileService(RepositoryFactory repositoryFactory)
|
||||
: this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory, new MacroService())
|
||||
: this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory, IMacroService macroService)
|
||||
public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory)
|
||||
{
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_macroService = macroService;
|
||||
_fileUowProvider = fileProvider;
|
||||
_dataUowProvider = dataProvider;
|
||||
}
|
||||
@@ -370,138 +362,6 @@ namespace Umbraco.Core.Services
|
||||
return template.IsValid();
|
||||
}
|
||||
|
||||
// TODO: Before making this public: How to get feedback in the UI when cancelled
|
||||
internal Attempt<PartialView> CreatePartialView(PartialView partialView)
|
||||
{
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(partialView.BasePath);
|
||||
var relativeFilePath = partialView.ParentFolderName.EnsureEndsWith('/') + partialViewsFileSystem.GetRelativePath(partialView.FileName);
|
||||
partialView.ReturnUrl = string.Format(partialView.EditViewFile + "?file={0}", HttpUtility.UrlEncode(relativeFilePath));
|
||||
|
||||
//return the link to edit the file if it already exists
|
||||
if (partialViewsFileSystem.FileExists(partialView.Path))
|
||||
return Attempt<PartialView>.Succeed(partialView);
|
||||
|
||||
if (CreatingPartialView.IsRaisedEventCancelled(new NewEventArgs<PartialView>(partialView, true, partialView.Alias, -1), this))
|
||||
{
|
||||
// We have nowhere to return to, clear ReturnUrl
|
||||
partialView.ReturnUrl = string.Empty;
|
||||
|
||||
var failureMessage = string.Format("Creating Partial View {0} was cancelled by an event handler.", partialViewsFileSystem.GetFullPath(partialView.FileName));
|
||||
LogHelper.Info<FileService>(failureMessage);
|
||||
|
||||
return Attempt<PartialView>.Fail(partialView, new ArgumentException(failureMessage));
|
||||
}
|
||||
|
||||
//create the file
|
||||
var snippetPathAttempt = partialView.TryGetSnippetPath(partialView.SnippetName);
|
||||
if (snippetPathAttempt.Success == false)
|
||||
{
|
||||
throw new InvalidOperationException("Could not load template with name " + partialView.SnippetName);
|
||||
}
|
||||
|
||||
using (var snippetFile = new StreamReader(partialViewsFileSystem.OpenFile(snippetPathAttempt.Result)))
|
||||
{
|
||||
var snippetContent = snippetFile.ReadToEnd().Trim();
|
||||
|
||||
//strip the @inherits if it's there
|
||||
snippetContent = partialView.HeaderMatch.Replace(snippetContent, string.Empty);
|
||||
|
||||
var content = string.Format("{0}{1}{2}", partialView.CodeHeader, Environment.NewLine, snippetContent);
|
||||
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
|
||||
{
|
||||
partialViewsFileSystem.AddFile(partialView.Path, stream);
|
||||
}
|
||||
}
|
||||
|
||||
if (partialView.CreateMacro)
|
||||
CreatePartialViewMacro(partialView);
|
||||
|
||||
CreatedPartialView.RaiseEvent(new NewEventArgs<PartialView>(partialView, false, partialView.Alias, -1), this);
|
||||
|
||||
return Attempt<PartialView>.Succeed(partialView);
|
||||
}
|
||||
|
||||
internal void CreatePartialViewMacro(PartialView partialView)
|
||||
{
|
||||
var name = partialView.FileName.Substring(0, (partialView.FileName.LastIndexOf('.') + 1))
|
||||
.Trim('.')
|
||||
.SplitPascalCasing()
|
||||
.ToFirstUpperInvariant();
|
||||
|
||||
var macro = new Macro(name, name) { ScriptPath = partialView.BasePath + partialView.FileName };
|
||||
_macroService.Save(macro);
|
||||
}
|
||||
|
||||
// TODO: Before making this public: How to get feedback in the UI when cancelled
|
||||
internal bool DeletePartialView(PartialView partialView, int userId = 0)
|
||||
{
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(partialView.BasePath);
|
||||
|
||||
if (DeletingPartialView.IsRaisedEventCancelled(new DeleteEventArgs<PartialView>(partialView), this))
|
||||
{
|
||||
LogHelper.Info<FileService>(string.Format("Deleting Partial View {0} was cancelled by an event handler.", partialViewsFileSystem.GetFullPath(partialView.FileName)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (partialViewsFileSystem.FileExists(partialView.FileName))
|
||||
{
|
||||
partialViewsFileSystem.DeleteFile(partialView.FileName);
|
||||
LogHelper.Info<FileService>(string.Format("Partial View file {0} deleted by user {1}", partialViewsFileSystem.GetFullPath(partialView.FileName), userId));
|
||||
}
|
||||
// TODO: does this ever even happen? I don't think folders show up in the tree currently.
|
||||
// Leaving this here as it was in the original PartialViewTasks code - SJ
|
||||
else if (partialViewsFileSystem.DirectoryExists(partialView.FileName))
|
||||
{
|
||||
partialViewsFileSystem.DeleteDirectory(partialView.FileName, true);
|
||||
LogHelper.Info<FileService>(string.Format("Partial View directory {0} deleted by user {1}", partialViewsFileSystem.GetFullPath(partialView.FileName), userId));
|
||||
}
|
||||
|
||||
DeletedPartialView.RaiseEvent(new DeleteEventArgs<PartialView>(partialView, false), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal Attempt<PartialView> SavePartialView(PartialView partialView, int userId = 0)
|
||||
{
|
||||
if (SavingPartialView.IsRaisedEventCancelled(new SaveEventArgs<PartialView>(partialView, true), this))
|
||||
{
|
||||
return Attempt<PartialView>.Fail(new ArgumentException("Save was cancelled by an event handler " + partialView.FileName));
|
||||
}
|
||||
|
||||
//Directory check.. only allow files in script dir and below to be edited
|
||||
if (partialView.IsValid() == false)
|
||||
{
|
||||
return Attempt<PartialView>.Fail(
|
||||
new ArgumentException(string.Format("Illegal path: {0} or illegal file extension {1}",
|
||||
partialView.Path,
|
||||
partialView.FileName.Substring(partialView.FileName.LastIndexOf(".", StringComparison.Ordinal)))));
|
||||
}
|
||||
|
||||
//NOTE: I've left the below here just for informational purposes. If we save a file this way, then the UTF8
|
||||
// BOM mucks everything up, strangely, if we use WriteAllText everything is ok!
|
||||
// http://issues.umbraco.org/issue/U4-2118
|
||||
//using (var sw = System.IO.File.CreateText(savePath))
|
||||
//{
|
||||
// sw.Write(val);
|
||||
//}
|
||||
|
||||
System.IO.File.WriteAllText(partialView.Path, partialView.Content, Encoding.UTF8);
|
||||
|
||||
//deletes the old file
|
||||
if (partialView.FileName != partialView.OldFileName)
|
||||
{
|
||||
// Create a new PartialView class so that we can set the FileName of the file that needs deleting
|
||||
var deletePartial = partialView;
|
||||
deletePartial.FileName = partialView.OldFileName;
|
||||
DeletePartialView(deletePartial, userId);
|
||||
}
|
||||
|
||||
SavedPartialView.RaiseEvent(new SaveEventArgs<PartialView>(partialView), this);
|
||||
|
||||
return Attempt.Succeed(partialView);
|
||||
}
|
||||
|
||||
//TODO Method to change name and/or alias of view/masterpage template
|
||||
|
||||
#region Event Handlers
|
||||
@@ -565,37 +425,6 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IFileService, SaveEventArgs<Stylesheet>> SavedStylesheet;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, SaveEventArgs<PartialView>> SavingPartialView;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, SaveEventArgs<PartialView>> SavedPartialView;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Create
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, NewEventArgs<PartialView>> CreatingPartialView;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Create
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, NewEventArgs<PartialView>> CreatedPartialView;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, DeleteEventArgs<PartialView>> DeletingPartialView;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IFileService, DeleteEventArgs<PartialView>> DeletedPartialView;
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
@@ -12,17 +11,6 @@ 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>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
/// assigned to an entity with a list of user id & permission pairs.
|
||||
/// </summary>
|
||||
/// <param name="permissionSet"></param>
|
||||
void ReplaceContentPermissions(EntityPermissionSet permissionSet);
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a single permission to the current content item for the specified user ids
|
||||
/// </summary>
|
||||
@@ -109,20 +97,6 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IContent> GetChildren(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the Parent to retrieve Children from</param>
|
||||
/// <param name="pageNumber">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records query would return without paging</param>
|
||||
/// <param name="orderBy">Field to order by</param>
|
||||
/// <param name="orderDirections">Direction to order by</param>
|
||||
/// <param name="filter">Search text filter</param>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IContent> GetPagedChildren(int id, int pageNumber, int pageSize, out int totalChildren,
|
||||
string orderBy, Direction orderDirection, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of an <see cref="IContent"/> objects versions by its Id
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user