Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16d98488e4 | |||
| 5a862f5c64 | |||
| caf41b74b8 | |||
| e858f4ff1e | |||
| 851961ef2b | |||
| c139c5d93e | |||
| c4603ee5cd | |||
| 1e9d1007b6 | |||
| f23af85253 | |||
| b0c5d16ba7 | |||
| cd5581bef6 | |||
| 7d8da56dcb | |||
| 0501b181ce | |||
| 3134668cec | |||
| a8cc8fa688 | |||
| cf2217cfe1 | |||
| e88e809e29 | |||
| c5559d7721 | |||
| 771e216813 | |||
| d33cc29f98 | |||
| b98fbbc75d | |||
| aba1862811 | |||
| 2f53698bc5 | |||
| e0e12d5773 | |||
| 2e96de5449 | |||
| c5bce3b8cc | |||
| b4286a7d4e | |||
| 31e018c045 | |||
| 50b71faff9 | |||
| 033fd1b7ff | |||
| 2621b25fd3 | |||
| 61055380c5 | |||
| adf09d925f | |||
| 12170fd787 | |||
| c32cae16db | |||
| 437d1b759b | |||
| f46811ff67 | |||
| 6d50b46bd8 | |||
| a51bf232ec | |||
| fd19411e65 | |||
| 7ac7bb9714 | |||
| 021cac9ca5 | |||
| 949447c25f | |||
| 99d7bf91b7 | |||
| a92c232178 | |||
| 30901446e1 | |||
| d772336c0f | |||
| f673cb024a | |||
| c4046ecb32 | |||
| 7823609500 | |||
| a88cb3f724 | |||
| 536b8ee921 | |||
| 3bf1041041 | |||
| fee23fac0a | |||
| cf6775ec31 | |||
| 1a13366cc9 | |||
| 595b039ebe | |||
| e6406e9757 | |||
| bf65bf5bab | |||
| 306d83cbd6 | |||
| 1da901be96 | |||
| d953966248 | |||
| 2f679a52f4 | |||
| ad8ab9f46e | |||
| 9b344a5a69 | |||
| 0a28bdafc2 | |||
| 7aa1f480c9 | |||
| 998f031877 | |||
| ac7cb8febe | |||
| 6e166bdba7 | |||
| 1f2e46a195 | |||
| bb22f5a283 | |||
| dcac5d4709 | |||
| 0e4e12d0ba | |||
| 9118701669 | |||
| 58758e7a41 | |||
| 4a516f7da9 | |||
| b7f7775b89 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
@ECHO OFF
|
||||
SET release=6.2.0
|
||||
SET comment=RC
|
||||
SET comment=
|
||||
SET version=%release%
|
||||
|
||||
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.7.1.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[4.5.11, 5.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[4.5.11, 6.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<UmbracoVersion>6.2.0-RC</UmbracoVersion>
|
||||
<UmbracoVersion>6.2.0</UmbracoVersion>
|
||||
</PropertyGroup>
|
||||
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core
|
||||
|
||||
@@ -53,5 +53,6 @@ namespace Umbraco.Core.Cache
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,12 @@ namespace Umbraco.Core.Cache
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeyExpression(regexString);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey);
|
||||
|
||||
@@ -171,6 +171,26 @@ namespace Umbraco.Core.Cache
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
var found = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
found.Add(c.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
|
||||
@@ -16,7 +16,15 @@ namespace Umbraco.Core.Cache
|
||||
void ClearCacheByKeySearch(string keyStartsWith);
|
||||
void ClearCacheByKeyExpression(string regexString);
|
||||
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
|
||||
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an item with a given key
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
object GetCacheItem(string cacheKey);
|
||||
|
||||
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,11 @@ namespace Umbraco.Core.Cache
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
return default(object);
|
||||
|
||||
@@ -114,6 +114,13 @@ namespace Umbraco.Core.Cache
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
|
||||
@@ -59,6 +59,13 @@ namespace Umbraco.Core.Cache
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = StaticCache[cacheKey];
|
||||
|
||||
@@ -1083,7 +1083,7 @@ namespace Umbraco.Core.Configuration
|
||||
bool globalPreviewEnabled = false;
|
||||
string value = GetKey("/settings/content/GlobalPreviewStorageEnabled");
|
||||
if (bool.TryParse(value, out globalPreviewEnabled))
|
||||
return !globalPreviewEnabled;
|
||||
return globalPreviewEnabled;
|
||||
// Return default
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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 "RC"; } }
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
|
||||
// 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
|
||||
|
||||
@@ -456,6 +456,11 @@ namespace Umbraco.Core
|
||||
string message;
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
|
||||
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
|
||||
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
|
||||
|
||||
/*
|
||||
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
if (supportsCaseInsensitiveQueries == false)
|
||||
{
|
||||
@@ -468,8 +473,9 @@ namespace Umbraco.Core
|
||||
|
||||
return new Result { Message = message, Success = false, Percentage = "15" };
|
||||
}
|
||||
*/
|
||||
|
||||
message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
|
||||
message = GetResultMessageForMySql();
|
||||
|
||||
var schemaResult = ValidateDatabaseSchema();
|
||||
var installedVersion = schemaResult.DetermineInstalledVersion();
|
||||
@@ -520,9 +526,9 @@ namespace Umbraco.Core
|
||||
LogHelper.Info<DatabaseContext>("Database upgrade started");
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
//var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
|
||||
var message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
|
||||
var message = GetResultMessageForMySql();
|
||||
|
||||
var schemaResult = ValidateDatabaseSchema();
|
||||
var installedVersion = schemaResult.DetermineInstalledVersion();
|
||||
@@ -549,6 +555,23 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
private string GetResultMessageForMySql()
|
||||
{
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider.GetType() == typeof(MySqlSyntaxProvider))
|
||||
{
|
||||
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
|
||||
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
|
||||
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
|
||||
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/*
|
||||
private string GetResultMessageForMySql(bool? supportsCaseInsensitiveQueries)
|
||||
{
|
||||
if (supportsCaseInsensitiveQueries == null)
|
||||
@@ -572,7 +595,7 @@ namespace Umbraco.Core
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}*/
|
||||
|
||||
private Attempt<Result> CheckReadyForInstall()
|
||||
{
|
||||
|
||||
@@ -36,42 +36,22 @@ namespace Umbraco.Core.Dynamics
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, IEnumerable<string> needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Any())
|
||||
{
|
||||
return needles.Any(haystack.Contains);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, params string[] needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Length > 0)
|
||||
{
|
||||
return needles.Any(haystack.Contains);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, StringComparison comparison, IEnumerable<string> needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Any())
|
||||
{
|
||||
return needles.Any(value => haystack.IndexOf(value, comparison) >= 0);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles, comparison);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsAny(this string haystack, StringComparison comparison, params string[] needles)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (!string.IsNullOrEmpty(haystack) || needles.Length > 0)
|
||||
{
|
||||
return needles.Any(value => haystack.IndexOf(value, comparison) >= 0);
|
||||
}
|
||||
return false;
|
||||
return StringExtensions.ContainsAny(haystack, needles, comparison);
|
||||
}
|
||||
[Obsolete("This method should not be used and will be removed in the future")]
|
||||
public static bool ContainsInsensitive(this string haystack, string needle)
|
||||
|
||||
@@ -4,14 +4,14 @@ using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
internal class PropertyResult : IPublishedProperty, IHtmlString
|
||||
internal class PropertyResult : IPublishedContentProperty, IHtmlString
|
||||
{
|
||||
private readonly IPublishedProperty _source;
|
||||
private readonly IPublishedContentProperty _source;
|
||||
private readonly string _alias;
|
||||
private readonly object _value;
|
||||
private readonly PropertyResultType _type;
|
||||
|
||||
internal PropertyResult(IPublishedProperty source, PropertyResultType type)
|
||||
internal PropertyResult(IPublishedContentProperty source, PropertyResultType type)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException("source");
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Events
|
||||
/// <remarks>
|
||||
/// This is protected so that inheritors can expose it with their own name
|
||||
/// </remarks>
|
||||
protected T EventObject { get; private set; }
|
||||
protected T EventObject { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
public MoveEventArgs(TEntity eventObject, bool canCancel, int parentId) : base(eventObject, canCancel)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
public class MoveEventInfo<TEntity>
|
||||
{
|
||||
public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
|
||||
{
|
||||
Entity = entity;
|
||||
OriginalPath = originalPath;
|
||||
NewParentId = newParentId;
|
||||
}
|
||||
|
||||
public MoveEventArgs(TEntity eventObject, int parentId) : base(eventObject)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
public TEntity Entity { get; set; }
|
||||
public string OriginalPath { get; set; }
|
||||
public int NewParentId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity being moved
|
||||
/// </summary>
|
||||
public TEntity Entity
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
/// </summary>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="moveInfo">
|
||||
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
|
||||
/// </param>
|
||||
public MoveEventArgs(bool canCancel, params MoveEventInfo<TEntity>[] moveInfo)
|
||||
: base(default(TEntity), canCancel)
|
||||
{
|
||||
if (moveInfo.FirstOrDefault() == null)
|
||||
{
|
||||
throw new ArgumentException("moveInfo argument must contain at least one item");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the Id of the objects new parent.
|
||||
/// </summary>
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
MoveInfoCollection = moveInfo;
|
||||
//assign the legacy props
|
||||
EventObject = moveInfo.First().Entity;
|
||||
ParentId = moveInfo.First().NewParentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
/// </summary>
|
||||
/// <param name="moveInfo">
|
||||
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
|
||||
/// </param>
|
||||
public MoveEventArgs(params MoveEventInfo<TEntity>[] moveInfo)
|
||||
: base(default(TEntity))
|
||||
{
|
||||
if (moveInfo.FirstOrDefault() == null)
|
||||
{
|
||||
throw new ArgumentException("moveInfo argument must contain at least one item");
|
||||
}
|
||||
|
||||
MoveInfoCollection = moveInfo;
|
||||
//assign the legacy props
|
||||
EventObject = moveInfo.First().Entity;
|
||||
ParentId = moveInfo.First().NewParentId;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload that specifies the MoveEventInfo object")]
|
||||
public MoveEventArgs(TEntity eventObject, bool canCancel, int parentId)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload that specifies the MoveEventInfo object")]
|
||||
public MoveEventArgs(TEntity eventObject, int parentId)
|
||||
: base(eventObject)
|
||||
{
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all MoveEventInfo objects used to create the object
|
||||
/// </summary>
|
||||
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity being moved
|
||||
/// </summary>
|
||||
[Obsolete("Retrieve the entity object from the MoveInfoCollection property instead")]
|
||||
public TEntity Entity
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Id of the object's new parent
|
||||
/// </summary>
|
||||
[Obsolete("Retrieve the ParentId from the MoveInfoCollection property instead")]
|
||||
public int ParentId { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -232,6 +232,7 @@ namespace Umbraco.Core.Models
|
||||
/// <remarks>
|
||||
/// This Property is kept internal until localization is introduced.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
internal string NodeName
|
||||
{
|
||||
get { return _nodeName; }
|
||||
@@ -248,6 +249,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Used internally to track if permissions have been changed during the saving process for this entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal bool PermissionsChanged
|
||||
{
|
||||
get { return _permissionsChanged; }
|
||||
@@ -314,6 +316,7 @@ namespace Umbraco.Core.Models
|
||||
PublishedState = state;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
internal PublishedState PublishedState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -333,26 +336,6 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the current entity
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent Clone()
|
||||
{
|
||||
var clone = (Content)this.MemberwiseClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.Version = Guid.NewGuid();
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
{
|
||||
property.ResetIdentity();
|
||||
property.Version = clone.Version;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current <see cref="IContent"/> entity is dirty.
|
||||
/// </summary>
|
||||
@@ -432,5 +415,47 @@ namespace Umbraco.Core.Models
|
||||
base.UpdatingEntity();
|
||||
Version = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContent Clone()
|
||||
{
|
||||
return DeepCloneWithResetIdentities();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContent DeepCloneWithResetIdentities()
|
||||
{
|
||||
var clone = (Content)DeepClone();
|
||||
clone.Key = Guid.Empty;
|
||||
clone.Version = Guid.NewGuid();
|
||||
clone.ResetIdentity();
|
||||
|
||||
foreach (var property in clone.Properties)
|
||||
{
|
||||
property.ResetIdentity();
|
||||
property.Version = clone.Version;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Content)base.DeepClone();
|
||||
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IContentType)ContentType.DeepClone();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,13 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Represents an abstract class for base Content properties and methods
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
[DebuggerDisplay("Id: {Id}, Name: {Name}, ContentType: {ContentTypeBase.Alias}")]
|
||||
public abstract class ContentBase : Entity, IContentBase
|
||||
{
|
||||
protected IContentTypeComposition ContentTypeBase;
|
||||
|
||||
private Lazy<int> _parentId;
|
||||
private string _name;//NOTE Once localization is introduced this will be the localized Name of the Content/Media.
|
||||
private int _sortOrder;
|
||||
@@ -257,6 +260,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
@@ -444,6 +448,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Returns a collection of the result of the last validation process, this collection contains all invalid properties.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal IEnumerable<Property> LastInvalidProperties
|
||||
{
|
||||
get { return _lastInvalidProperties; }
|
||||
@@ -465,5 +470,6 @@ namespace Umbraco.Core.Models
|
||||
prop.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used content repository in order to add an entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentPreviewEntity<TContent> : ContentXmlEntity<TContent>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentPreviewEntity(bool previewExists, TContent content, Func<TContent, XElement> xml)
|
||||
: base(previewExists, content, xml)
|
||||
{
|
||||
Version = content.Version;
|
||||
}
|
||||
|
||||
public Guid Version { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -120,38 +120,6 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the current entity
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
var clone = (ContentType)this.MemberwiseClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
var propertyGroups = this.PropertyGroups.Select(x => x.Clone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
clone.PropertyTypes = this.PropertyTypeCollection.Select(x => x.Clone()).ToList();
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyType in clone.PropertyTypes.Where(x => x.HasIdentity))
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
@@ -173,5 +141,53 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentType)base.DeepClone();
|
||||
var propertyGroups = PropertyGroups.Select(x => (PropertyGroup)x.DeepClone()).ToList();
|
||||
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
|
||||
//set the property types that are not part of a group
|
||||
clone.PropertyTypes = PropertyTypeCollection
|
||||
.Where(x => x.PropertyGroupId == null)
|
||||
.Select(x => (PropertyType)x.DeepClone()).ToList();
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use DeepCloneWithResetIdentities instead")]
|
||||
public IContentType Clone(string alias)
|
||||
{
|
||||
return DeepCloneWithResetIdentities(alias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IContentType DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (ContentType)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
propertyGroup.ResetDirtyProperties(false);
|
||||
}
|
||||
foreach (var propertyType in clone.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -173,7 +174,8 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value.ToSafeAlias();
|
||||
//_alias = value.ToSafeAlias();
|
||||
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
@@ -319,7 +321,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
private IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition
|
||||
{
|
||||
private readonly List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
private List<IContentTypeComposition> _contentTypeComposition = new List<IContentTypeComposition>();
|
||||
internal List<int> RemovedContentTypeKeyTracker = new List<int>();
|
||||
|
||||
protected ContentTypeCompositionBase(int parentId) : base(parentId)
|
||||
@@ -217,5 +217,17 @@ namespace Umbraco.Core.Models
|
||||
.Select(x => x.Id)
|
||||
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeCompositionBase)base.DeepClone();
|
||||
|
||||
//need to manually assign since this is an internal field and will not be automatically mapped
|
||||
clone.RemovedContentTypeKeyTracker = new List<int>();
|
||||
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Represents a POCO for setting sort order on a ContentType reference
|
||||
/// </summary>
|
||||
public class ContentTypeSort : IValueObject
|
||||
public class ContentTypeSort : IValueObject, IDeepCloneable
|
||||
{
|
||||
public ContentTypeSort()
|
||||
{
|
||||
@@ -33,5 +33,36 @@ namespace Umbraco.Core.Models
|
||||
/// Gets or sets the Alias of the ContentType
|
||||
/// </summary>
|
||||
public string Alias { get; set; }
|
||||
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentTypeSort)MemberwiseClone();
|
||||
var id = Id.Value;
|
||||
clone.Id = new Lazy<int>(() => id);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(ContentTypeSort other)
|
||||
{
|
||||
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
|
||||
}
|
||||
|
||||
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((ContentTypeSort) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Id.GetHashCode()*397) ^ Alias.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in content/media/member repositories in order to add this type of entity to the persisted collection to be saved
|
||||
/// in a single transaction during saving an entity
|
||||
/// </summary>
|
||||
internal class ContentXmlEntity<TContent> : IAggregateRoot
|
||||
where TContent : IContentBase
|
||||
{
|
||||
private readonly bool _entityExists;
|
||||
private readonly Func<TContent, XElement> _xml;
|
||||
|
||||
public ContentXmlEntity(bool entityExists, TContent content, Func<TContent, XElement> xml)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
_entityExists = entityExists;
|
||||
_xml = xml;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public ContentXmlEntity(TContent content)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public XElement Xml
|
||||
{
|
||||
get { return _xml(Content); }
|
||||
}
|
||||
public TContent Content { get; private set; }
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return Content.Id; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public Guid Key { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
|
||||
public bool HasIdentity
|
||||
{
|
||||
get { return _entityExists; }
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var clone = (ContentXmlEntity<TContent>)MemberwiseClone();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class DeepCloneHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
public static void DeepCloneRefProperties(IDeepCloneable input, IDeepCloneable output)
|
||||
{
|
||||
var inputType = input.GetType();
|
||||
var outputType = output.GetType();
|
||||
|
||||
if (inputType != outputType)
|
||||
{
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
var refProperties = inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//reference type but not string
|
||||
x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false);
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
|
||||
{
|
||||
//this ref property is also deep cloneable so clone it
|
||||
var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
//set the cloned value to the property
|
||||
propertyInfo.SetValue(output, result.DeepClone(), null);
|
||||
}
|
||||
}
|
||||
else if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
|
||||
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
|
||||
{
|
||||
IList newList;
|
||||
if (propertyInfo.PropertyType.IsGenericType
|
||||
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||
{
|
||||
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
|
||||
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
|
||||
newList = (IList)Activator.CreateInstance(genericType);
|
||||
}
|
||||
else if (propertyInfo.PropertyType.IsArray
|
||||
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
|
||||
{
|
||||
//if its an array, we'll create a list to work with first and then convert to array later
|
||||
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
|
||||
newList = new List<object>();
|
||||
}
|
||||
else
|
||||
{
|
||||
//its a custom IEnumerable, we'll try to create it
|
||||
try
|
||||
{
|
||||
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
|
||||
//if it's an IList we can work with it, otherwise we cannot
|
||||
newList = custom as IList;
|
||||
if (newList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//could not create this type so we'll skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
|
||||
if (enumerable == null) continue;
|
||||
|
||||
var isUsableType = true;
|
||||
|
||||
//now clone each item
|
||||
foreach (var o in enumerable)
|
||||
{
|
||||
//first check if the item is deep cloneable and copy that way
|
||||
var dc = o as IDeepCloneable;
|
||||
if (dc != null)
|
||||
{
|
||||
newList.Add(dc.DeepClone());
|
||||
}
|
||||
else if (o is string || o.GetType().IsValueType)
|
||||
{
|
||||
//check if the item is a value type or a string, then we can just use it
|
||||
newList.Add(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
//this will occur if the item is not a string or value type or IDeepCloneable, in this case we cannot
|
||||
// clone each element, we'll need to skip this property, people will have to manually clone this list
|
||||
isUsableType = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if this was not usable, skip this property
|
||||
if (isUsableType == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyInfo.PropertyType.IsArray)
|
||||
{
|
||||
//need to convert to array
|
||||
var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
|
||||
for (int i = 0; i < newList.Count; i++)
|
||||
{
|
||||
arr[i] = newList[i];
|
||||
}
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, arr, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, newList, null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -96,5 +97,6 @@ namespace Umbraco.Core.Models
|
||||
if(ParentId == Guid.Empty)
|
||||
_parentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -65,5 +65,6 @@ namespace Umbraco.Core.Models
|
||||
}, _value, ValueSelector);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
@@ -93,7 +96,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// /// Gets or sets the WasCancelled flag, which is used to track
|
||||
/// Gets or sets the WasCancelled flag, which is used to track
|
||||
/// whether some action against an entity was cancelled through some event.
|
||||
/// This only exists so we have a way to check if an event was cancelled through
|
||||
/// the new api, which also needs to take effect in the legacy api.
|
||||
@@ -155,6 +158,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity has an identity, eg. Id.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual bool HasIdentity
|
||||
{
|
||||
get
|
||||
@@ -226,5 +230,62 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
|
||||
return _hash.Value;
|
||||
}
|
||||
|
||||
public virtual object DeepClone()
|
||||
{
|
||||
//Memberwise clone on Entity will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var clone = (Entity)MemberwiseClone();
|
||||
//Automatically deep clone ref properties that are IDeepCloneable
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
|
||||
//Using data contract serializer - has issues
|
||||
|
||||
//var s = Serialize(this);
|
||||
//var d = Deserialize(s, this.GetType());
|
||||
//return d;
|
||||
|
||||
//Using binary serializer - has issues
|
||||
|
||||
//using (var memoryStream = new MemoryStream(10))
|
||||
//{
|
||||
//IFormatter formatter = new BinaryFormatter();
|
||||
//formatter.Serialize(memoryStream, this);
|
||||
//memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
//return formatter.Deserialize(memoryStream);
|
||||
//}
|
||||
}
|
||||
|
||||
// serialize/deserialize with data contracts:
|
||||
|
||||
//public static string Serialize(object obj)
|
||||
//{
|
||||
// using (var memoryStream = new MemoryStream())
|
||||
// using (var reader = new StreamReader(memoryStream))
|
||||
// {
|
||||
// var serializer = new DataContractSerializer(obj.GetType());
|
||||
// serializer.WriteObject(memoryStream, obj);
|
||||
// memoryStream.Position = 0;
|
||||
// return reader.ReadToEnd();
|
||||
// }
|
||||
//}
|
||||
|
||||
//public static object Deserialize(string xml, Type toType)
|
||||
//{
|
||||
// using (Stream stream = new MemoryStream())
|
||||
// {
|
||||
// using (var writer = new StreamWriter(stream, Encoding.UTF8))
|
||||
// {
|
||||
// writer.Write(xml);
|
||||
// //byte[] data = Encoding.UTF8.GetBytes(xml);
|
||||
// //stream.Write(data, 0, data.Length);
|
||||
// stream.Position = 0;
|
||||
// var deserializer = new DataContractSerializer(toType);
|
||||
// return deserializer.ReadObject(stream);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
/// </summary>
|
||||
/// <remarks>The current database schema doesn't provide a modified date
|
||||
/// for all entities, so this will have to be changed at a later stage.</remarks>
|
||||
public interface IEntity
|
||||
public interface IEntity : IDeepCloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id of the entity
|
||||
|
||||
@@ -3,12 +3,15 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for use to implement IRememberBeingDirty/ICanBeDirty
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public abstract class TracksChangesEntityBase : IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -6,16 +6,6 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal sealed class Folder : Entity
|
||||
{
|
||||
public Folder(string folderPath)
|
||||
{
|
||||
Path = folderPath;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
|
||||
/// </summary>
|
||||
@@ -114,5 +104,18 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <returns>True if file is valid, otherwise false</returns>
|
||||
public abstract bool IsValid();
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (File)base.DeepClone();
|
||||
|
||||
//need to manually assign since they are readonly properties
|
||||
clone._alias = Alias;
|
||||
clone._name = Name;
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal sealed class Folder : Entity
|
||||
{
|
||||
public Folder(string folderPath)
|
||||
{
|
||||
Path = folderPath;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -72,5 +72,11 @@ namespace Umbraco.Core.Models
|
||||
/// Changes the Published state of the content object
|
||||
/// </summary>
|
||||
void ChangePublishedState(PublishedState state);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IContent DeepCloneWithResetIdentities();
|
||||
}
|
||||
}
|
||||
@@ -29,5 +29,12 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="template"><see cref="ITemplate"/> to remove</param>
|
||||
/// <returns>True if template was removed, otherwise False</returns>
|
||||
bool RemoveTemplate(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IContentType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IDeepCloneable
|
||||
{
|
||||
object DeepClone();
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace Umbraco.Core.Models
|
||||
/// <para>The properties collection of an IPublishedContent instance should be read-only ie it is illegal
|
||||
/// to add properties to the collection.</para>
|
||||
/// </remarks>
|
||||
ICollection<IPublishedProperty> Properties { get; }
|
||||
ICollection<IPublishedContentProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
@@ -119,7 +119,7 @@ namespace Umbraco.Core.Models
|
||||
/// <para>otherwise return a property -- that may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias);
|
||||
IPublishedContentProperty GetProperty(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
@@ -133,7 +133,7 @@ namespace Umbraco.Core.Models
|
||||
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias, bool recurse);
|
||||
IPublishedContentProperty GetProperty(string alias, bool recurse);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a property identified by its alias.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedProperty
|
||||
public interface IPublishedContentProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the alias of the property.
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class Member : ContentBase, IMember
|
||||
{
|
||||
private readonly IMemberType _contentType;
|
||||
private IMemberType _contentType;
|
||||
private readonly string _contentTypeAlias;
|
||||
private string _username;
|
||||
private string _email;
|
||||
@@ -153,7 +153,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Gets or sets the raw password value
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public string RawPasswordValue
|
||||
{
|
||||
get { return _rawPasswordValue; }
|
||||
@@ -180,7 +180,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public string PasswordQuestion
|
||||
{
|
||||
get
|
||||
@@ -240,7 +240,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoCommentPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public string Comments
|
||||
{
|
||||
get
|
||||
@@ -269,7 +269,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoApprovePropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public bool IsApproved
|
||||
{
|
||||
get
|
||||
@@ -304,7 +304,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoLockPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public bool IsLockedOut
|
||||
{
|
||||
get
|
||||
@@ -336,7 +336,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoLastLoginPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public DateTime LastLoginDate
|
||||
{
|
||||
get
|
||||
@@ -368,7 +368,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoMemberLastPasswordChange
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public DateTime LastPasswordChangeDate
|
||||
{
|
||||
get
|
||||
@@ -400,7 +400,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoMemberLastLockout
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public DateTime LastLockoutDate
|
||||
{
|
||||
get
|
||||
@@ -433,7 +433,7 @@ namespace Umbraco.Core.Models
|
||||
/// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[DataMember]
|
||||
public int FailedPasswordAttempts
|
||||
{
|
||||
get
|
||||
@@ -554,11 +554,17 @@ namespace Umbraco.Core.Models
|
||||
/* Internal experiment - only used for mapping queries.
|
||||
* Adding these to have first level properties instead of the Properties collection.
|
||||
*/
|
||||
[IgnoreDataMember]
|
||||
internal string LongStringPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal string ShortStringPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal int IntegerropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal bool BoolPropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal DateTime DateTimePropertyValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
internal string PropertyTypeAlias { get; set; }
|
||||
|
||||
private Attempt<T> WarnIfPropertyTypeNotFoundOnGet<T>(string propertyAlias, string propertyName, T defaultVal)
|
||||
@@ -628,5 +634,17 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Member)base.DeepClone();
|
||||
|
||||
//need to manually clone this since it's not settable
|
||||
clone._contentType = (IMemberType)ContentType.DeepClone();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class User : TracksChangesEntityBase, IUser
|
||||
public class User : Entity, IUser
|
||||
{
|
||||
public User(IUserType userType)
|
||||
{
|
||||
@@ -57,8 +57,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
}
|
||||
|
||||
private IUserType _userType;
|
||||
private bool _hasIdentity;
|
||||
private int _id;
|
||||
private string _name;
|
||||
private Type _userTypeKey;
|
||||
private readonly List<string> _addedSections;
|
||||
@@ -81,7 +79,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartContentId);
|
||||
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartMediaId);
|
||||
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.AllowedSections);
|
||||
private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<User, object>(x => x.Id);
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Name);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<User, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
@@ -93,52 +90,8 @@ namespace Umbraco.Core.Models.Membership
|
||||
private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Language);
|
||||
private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.DefaultPermissions);
|
||||
private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.DefaultToLiveEditing);
|
||||
private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.HasIdentity);
|
||||
private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo<User, IUserType>(x => x.UserType);
|
||||
|
||||
#region Implementation of IEntity
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool HasIdentity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hasIdentity;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_hasIdentity = value;
|
||||
return _hasIdentity;
|
||||
}, _hasIdentity, HasIdentitySelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_id = value;
|
||||
HasIdentity = true; //set the has Identity
|
||||
return _id;
|
||||
}, _id, IdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
//this doesn't get used
|
||||
[IgnoreDataMember]
|
||||
public Guid Key { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation of IMembershipUser
|
||||
|
||||
[IgnoreDataMember]
|
||||
@@ -256,11 +209,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
[IgnoreDataMember]
|
||||
public string RawPasswordAnswerValue { get; set; }
|
||||
[IgnoreDataMember]
|
||||
public string Comments { get; set; }
|
||||
[IgnoreDataMember]
|
||||
public DateTime CreateDate { get; set; }
|
||||
[IgnoreDataMember]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public string Comments { get; set; }
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
[IgnoreDataMember]
|
||||
@@ -491,7 +440,7 @@ namespace Umbraco.Core.Models.Membership
|
||||
_removedSections.Add(e.OldItems.Cast<string>().First());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Internal class used to wrap the user in a profile
|
||||
/// </summary>
|
||||
@@ -515,6 +464,24 @@ namespace Umbraco.Core.Models.Membership
|
||||
get { return _user.Name; }
|
||||
set { _user.Name = value; }
|
||||
}
|
||||
|
||||
protected bool Equals(UserProfile other)
|
||||
{
|
||||
return _user.Equals(other._user);
|
||||
}
|
||||
|
||||
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((UserProfile) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _user.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
/// <summary>
|
||||
/// The value stored for the pre-value field
|
||||
/// </summary>
|
||||
public string Value { get; private set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The database id for the pre-value field value
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class Property : Entity
|
||||
{
|
||||
private readonly PropertyType _propertyType;
|
||||
private PropertyType _propertyType;
|
||||
private Guid _version;
|
||||
private object _value;
|
||||
|
||||
@@ -147,5 +147,16 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
return _propertyType.IsPropertyValueValid(value);
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Property)base.DeepClone();
|
||||
|
||||
//need to manually assign since this is a readonly property
|
||||
clone._propertyType = (PropertyType)PropertyType.DeepClone();
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged
|
||||
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
|
||||
internal Action OnAdd;
|
||||
@@ -195,5 +196,19 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a deep clone of this property collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object DeepClone()
|
||||
{
|
||||
var newList = new PropertyCollection();
|
||||
foreach (var p in this)
|
||||
{
|
||||
newList.Add((Property)p.DeepClone());
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,42 +22,10 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>Xml of the property and its value</returns>
|
||||
public static XElement ToXml(this Property property)
|
||||
{
|
||||
return property.ToXml(ApplicationContext.Current.Services.DataTypeService);
|
||||
var xmlSerializer = new EntityXmlSerializer();
|
||||
return xmlSerializer.Serialize(ApplicationContext.Current.Services.DataTypeService, property);
|
||||
}
|
||||
|
||||
internal static XElement ToXml(this Property property, IDataTypeService dataTypeService)
|
||||
{
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
|
||||
var xd = new XmlDocument();
|
||||
var xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
|
||||
//Add the property alias to the legacy schema
|
||||
if (UmbracoSettings.UseLegacyXmlSchema)
|
||||
{
|
||||
var alias = xd.CreateAttribute("alias");
|
||||
alias.Value = property.Alias.ToSafeAlias();
|
||||
xmlNode.Attributes.Append(alias);
|
||||
}
|
||||
|
||||
//This seems to fail during testing
|
||||
//SD: With the new null checks below, this shouldn't fail anymore.
|
||||
var dt = property.PropertyType.DataType(property.Id, dataTypeService);
|
||||
if (dt != null && dt.Data != null)
|
||||
{
|
||||
//We've already got the value for the property so we're going to give it to the
|
||||
// data type's data property so it doesn't go re-look up the value from the db again.
|
||||
var defaultData = dt.Data as IDataValueSetter;
|
||||
if (defaultData != null)
|
||||
{
|
||||
defaultData.SetValue(property.Value, property.PropertyType.DataTypeDatabaseType.ToString());
|
||||
}
|
||||
|
||||
xmlNode.AppendChild(dt.Data.ToXMl(xd));
|
||||
}
|
||||
|
||||
var element = xmlNode.GetXElement();
|
||||
return element;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -107,23 +107,6 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
internal PropertyGroup Clone()
|
||||
{
|
||||
var clone = (PropertyGroup) this.MemberwiseClone();
|
||||
var collection = new PropertyTypeCollection();
|
||||
foreach (var propertyType in this.PropertyTypes)
|
||||
{
|
||||
var property = propertyType.Clone();
|
||||
property.ResetIdentity();
|
||||
property.ResetDirtyProperties(false);
|
||||
collection.Add(property);
|
||||
}
|
||||
clone.PropertyTypes = collection;
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ParentId from the lazy integer id
|
||||
/// </summary>
|
||||
@@ -157,5 +140,6 @@ namespace Umbraco.Core.Models
|
||||
//Calculate the hash code for the product.
|
||||
return hashName ^ hashId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -13,9 +14,10 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract]
|
||||
public class PropertyGroupCollection : KeyedCollection<string, PropertyGroup>, INotifyCollectionChanged
|
||||
public class PropertyGroupCollection : KeyedCollection<string, PropertyGroup>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
|
||||
|
||||
internal Action OnAdd;
|
||||
|
||||
internal PropertyGroupCollection()
|
||||
@@ -131,5 +133,15 @@ namespace Umbraco.Core.Models
|
||||
CollectionChanged(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var newGroup = new PropertyGroupCollection();
|
||||
foreach (var p in this)
|
||||
{
|
||||
newGroup.Add((PropertyGroup)p.DeepClone());
|
||||
}
|
||||
return newGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -94,7 +95,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value;
|
||||
_alias = value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase);
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
@@ -388,7 +389,9 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//TODO: We must ensure that the property value can actually be saved based on the specified database type
|
||||
|
||||
//TODO Add PropertyEditor validation when its relevant to introduce
|
||||
/*if (value is IEditorModel && DataTypeControlId != Guid.Empty)
|
||||
{
|
||||
@@ -402,14 +405,6 @@ namespace Umbraco.Core.Models
|
||||
return true;
|
||||
}
|
||||
|
||||
internal PropertyType Clone()
|
||||
{
|
||||
var clone = (PropertyType) this.MemberwiseClone();
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public bool Equals(PropertyType other)
|
||||
{
|
||||
//Check whether the compared object is null.
|
||||
@@ -433,5 +428,20 @@ namespace Umbraco.Core.Models
|
||||
//Calculate the hash code for the product.
|
||||
return hashName ^ hashAlias;
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (PropertyType)base.DeepClone();
|
||||
|
||||
//need to manually assign the Lazy value as it will not be automatically mapped
|
||||
if (PropertyGroupId != null)
|
||||
{
|
||||
clone._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
|
||||
}
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -13,9 +14,12 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract]
|
||||
public class PropertyTypeCollection : KeyedCollection<string, PropertyType>, INotifyCollectionChanged
|
||||
public class PropertyTypeCollection : KeyedCollection<string, PropertyType>, INotifyCollectionChanged, IDeepCloneable
|
||||
{
|
||||
[IgnoreDataMember]
|
||||
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
|
||||
|
||||
[IgnoreDataMember]
|
||||
internal Action OnAdd;
|
||||
|
||||
internal PropertyTypeCollection()
|
||||
@@ -131,5 +135,15 @@ namespace Umbraco.Core.Models
|
||||
CollectionChanged(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
public object DeepClone()
|
||||
{
|
||||
var newGroup = new PropertyTypeCollection();
|
||||
foreach (var p in this)
|
||||
{
|
||||
newGroup.Add((PropertyType)p.DeepClone());
|
||||
}
|
||||
return newGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// Adds a property to the extended content.
|
||||
/// </summary>
|
||||
/// <param name="property">The property to add.</param>
|
||||
void AddProperty(IPublishedProperty property);
|
||||
void AddProperty(IPublishedContentProperty property);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether properties were added to the extended content.
|
||||
|
||||
@@ -66,10 +66,10 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
#region IPublishedContentExtended
|
||||
|
||||
void IPublishedContentExtended.AddProperty(IPublishedProperty property)
|
||||
void IPublishedContentExtended.AddProperty(IPublishedContentProperty property)
|
||||
{
|
||||
if (_properties == null)
|
||||
_properties = new Collection<IPublishedProperty>();
|
||||
_properties = new Collection<IPublishedContentProperty>();
|
||||
_properties.Add(property);
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
#region Properties
|
||||
|
||||
private ICollection<IPublishedProperty> _properties;
|
||||
private ICollection<IPublishedContentProperty> _properties;
|
||||
|
||||
public override ICollection<IPublishedProperty> Properties
|
||||
public override ICollection<IPublishedContentProperty> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -138,7 +138,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
}
|
||||
|
||||
public override IPublishedProperty GetProperty(string alias)
|
||||
public override IPublishedContentProperty GetProperty(string alias)
|
||||
{
|
||||
return _properties == null
|
||||
? Content.GetProperty(alias)
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
|
||||
#region Properties
|
||||
|
||||
public virtual ICollection<IPublishedProperty> Properties
|
||||
public virtual ICollection<IPublishedContentProperty> Properties
|
||||
{
|
||||
get { return Content.Properties; }
|
||||
}
|
||||
@@ -193,12 +193,12 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
get { return Content[alias]; }
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias)
|
||||
public virtual IPublishedContentProperty GetProperty(string alias)
|
||||
{
|
||||
return Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
|
||||
public virtual IPublishedContentProperty GetProperty(string alias, bool recurse)
|
||||
{
|
||||
return Content.GetProperty(alias, recurse);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// Provides a base class for <c>IPublishedProperty</c> implementations which converts and caches
|
||||
/// the value source to the actual value to use when rendering content.
|
||||
/// </summary>
|
||||
internal abstract class PublishedPropertyBase : IPublishedProperty
|
||||
internal abstract class PublishedPropertyBase : IPublishedContentProperty
|
||||
{
|
||||
public readonly PublishedPropertyType PropertyType;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using Umbraco.Core.PropertyEditors;
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IPublishedProperty"/> type.
|
||||
/// Represents an <see cref="IPublishedContentProperty"/> type.
|
||||
/// </summary>
|
||||
/// <remarks>Instances of the <see cref="PublishedPropertyType"/> class are immutable, ie
|
||||
/// if the property type changes, then a new class needs to be created.</remarks>
|
||||
|
||||
@@ -107,5 +107,6 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get { return _relationType.Id; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -127,5 +127,6 @@ namespace Umbraco.Core.Models
|
||||
}, _childObjectType, ChildObjectTypeSelector);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
@@ -34,7 +35,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
UpdateDate = updateDate;
|
||||
CreateDate = createDate;
|
||||
Key = Id.ToString().EncodeAsGuid();
|
||||
Key = Id.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
|
||||
Id = id;
|
||||
ServerAddress = serverAddress;
|
||||
ComputerName = computerName;
|
||||
@@ -51,7 +52,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
CreateDate = createDate;
|
||||
UpdateDate = createDate;
|
||||
Key = 0.ToString().EncodeAsGuid();
|
||||
Key = 0.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
|
||||
ServerAddress = serverAddress;
|
||||
ComputerName = computerName;
|
||||
}
|
||||
|
||||
@@ -132,5 +132,6 @@ namespace Umbraco.Core.Models
|
||||
}, _comment, CommentSelector);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -14,20 +16,18 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class Template : File, ITemplate
|
||||
{
|
||||
private readonly string _alias;
|
||||
private readonly string _name;
|
||||
private string _alias;
|
||||
private string _name;
|
||||
private int _creatorId;
|
||||
private int _level;
|
||||
private int _sortOrder;
|
||||
private int _parentId;
|
||||
private int _masterTemplateId;
|
||||
private string _masterTemplateAlias;
|
||||
|
||||
private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.CreatorId);
|
||||
private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.Level);
|
||||
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.SortOrder);
|
||||
private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.ParentId);
|
||||
//private static readonly PropertyInfo MasterTemplateIdSelector = ExpressionHelper.GetPropertyInfo<Template, int>(x => x.MasterTemplateId);
|
||||
private static readonly PropertyInfo MasterTemplateAliasSelector = ExpressionHelper.GetPropertyInfo<Template, string>(x => x.MasterTemplateAlias);
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
base.Path = path;
|
||||
ParentId = -1;
|
||||
_name = name.Replace("/", ".").Replace("\\", "");
|
||||
_alias = alias.ToSafeAlias();
|
||||
_name = name; //.Replace("/", ".").Replace("\\", ""); // why? that's just the name!
|
||||
_alias = alias.ToCleanString(CleanStringType.UnderscoreAlias);
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
@@ -197,5 +197,18 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
MasterTemplateId = new Lazy<int>(() => masterTemplate.Id);
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (Template)base.DeepClone();
|
||||
|
||||
//need to manually assign since they are readonly properties
|
||||
clone._alias = Alias;
|
||||
clone._name = Name;
|
||||
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
@@ -46,11 +47,13 @@ namespace Umbraco.Core.Models
|
||||
public UmbracoEntity()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
UmbracoProperties = new List<UmbracoProperty>();
|
||||
}
|
||||
|
||||
public UmbracoEntity(bool trashed)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
UmbracoProperties = new List<UmbracoProperty>();
|
||||
Trashed = trashed;
|
||||
}
|
||||
|
||||
@@ -58,6 +61,7 @@ namespace Umbraco.Core.Models
|
||||
public UmbracoEntity(UInt64 trashed)
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
UmbracoProperties = new List<UmbracoProperty>();
|
||||
Trashed = trashed == 1;
|
||||
}
|
||||
|
||||
@@ -287,11 +291,39 @@ namespace Umbraco.Core.Models
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
public IList<UmbracoProperty> UmbracoProperties { get; set; }
|
||||
|
||||
internal class UmbracoProperty
|
||||
|
||||
internal class UmbracoProperty : IDeepCloneable
|
||||
{
|
||||
public Guid DataTypeControlId { get; set; }
|
||||
public string Value { get; set; }
|
||||
public object DeepClone()
|
||||
{
|
||||
//Memberwise clone on Entity will work since it doesn't have any deep elements
|
||||
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
|
||||
var clone = MemberwiseClone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected bool Equals(UmbracoProperty other)
|
||||
{
|
||||
return DataTypeControlId.Equals(other.DataTypeControlId) && string.Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
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((UmbracoProperty) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (DataTypeControlId.GetHashCode()*397) ^ (Value != null ? Value.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,10 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
var containsKey = _cache.ContainsKey(compositeKey);
|
||||
if (containsKey)
|
||||
{
|
||||
return _cache[compositeKey];
|
||||
var result = _cache[compositeKey];
|
||||
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
return (IEntity)result.DeepClone();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -56,7 +59,12 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
into key
|
||||
let containsKey = _cache.ContainsKey(key)
|
||||
where containsKey
|
||||
select _cache[key]).ToList();
|
||||
select _cache[key]
|
||||
into result
|
||||
//don't return null objects
|
||||
where result != null
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
select (IEntity)result.DeepClone()).ToList();
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -67,7 +75,14 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IEntity> GetAllByType(Type type)
|
||||
{
|
||||
var list = _cache.Keys.Where(key => key.Contains(type.Name)).Select(key => _cache[key]).ToList();
|
||||
var list = _cache.Keys
|
||||
.Where(key => key.Contains(type.Name))
|
||||
.Select(key => _cache[key])
|
||||
//don't return null objects
|
||||
.Where(result => result != null)
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
.Select(result => (IEntity)result.DeepClone())
|
||||
.ToList();
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -78,6 +93,9 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
/// <param name="entity"></param>
|
||||
public void Save(Type type, IEntity entity)
|
||||
{
|
||||
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
|
||||
entity = (IEntity)entity.DeepClone();
|
||||
|
||||
_cache.AddOrUpdate(GetCompositeId(type, entity.Id), entity, (x, y) => entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@@ -29,6 +29,9 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
/// when there are async requests being made even in the context of a web request, the HttpContext.Current will be null but the HttpRuntime.Cache will
|
||||
/// always be available.
|
||||
///
|
||||
/// TODO: Each item that get's added to this cache will be a clone of the original with it's dirty properties reset, and every item that is resolved from the cache
|
||||
/// is a clone of the item that is in there, otherwise we end up with thread safety issues since multiple thread would be working on the exact same entity at the same time.
|
||||
///
|
||||
/// </remarks>
|
||||
internal sealed class RuntimeCacheProvider : IRepositoryCacheProvider
|
||||
{
|
||||
@@ -64,8 +67,11 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
{
|
||||
//ensure the key doesn't exist anymore in the tracker
|
||||
_keyTracker.Remove(key);
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
return (IEntity)result.DeepClone();
|
||||
}
|
||||
|
||||
public IEnumerable<IEntity> GetByIds(Type type, List<Guid> ids)
|
||||
@@ -85,7 +91,8 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Add(result);
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
collection.Add((IEntity)result.DeepClone());
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
@@ -110,7 +117,8 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Add(result);
|
||||
//IMPORTANT: we must clone to resolve, see: http://issues.umbraco.org/issue/U4-4259
|
||||
collection.Add((IEntity)result.DeepClone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +127,9 @@ namespace Umbraco.Core.Persistence.Caching
|
||||
|
||||
public void Save(Type type, IEntity entity)
|
||||
{
|
||||
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
|
||||
entity = (IEntity)entity.DeepClone();
|
||||
|
||||
var key = GetCompositeId(type, entity.Id);
|
||||
|
||||
_keyTracker.TryAdd(key);
|
||||
|
||||
@@ -176,6 +176,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false).ToList();
|
||||
|
||||
//Add valid and invalid foreign key differences to the result object
|
||||
// We'll need to do invariant contains with case insensitivity because foreign key, primary key, and even index naming w/ MySQL is not standardized
|
||||
// In theory you could have: FK_ or fk_ ...or really any standard that your development department (or developer) chooses to use.
|
||||
foreach (var unknown in unknownConstraintsInDatabase)
|
||||
{
|
||||
if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown))
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return new Version(4, 7, 0);
|
||||
}
|
||||
|
||||
return new Version(4, 9, 0);
|
||||
return new Version(4, 8, 0);
|
||||
}
|
||||
|
||||
//if the error is for umbracoServer
|
||||
|
||||
@@ -16,8 +16,21 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
ProductName = product;
|
||||
}
|
||||
|
||||
public MigrationAttribute(string minimumCurrentVersion, string targetVersion, int sortOrder, string product)
|
||||
{
|
||||
TargetVersion = new Version(targetVersion);
|
||||
MinimumCurrentVersion = new Version(minimumCurrentVersion);
|
||||
SortOrder = sortOrder;
|
||||
ProductName = product;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target version of this migration.
|
||||
/// Gets the minimum current version for which this migration is allowed to execute
|
||||
/// </summary>
|
||||
public Version MinimumCurrentVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target version of this migration.
|
||||
/// </summary>
|
||||
public Version TargetVersion { get; private set; }
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
/// </summary>
|
||||
public class MigrationRunner
|
||||
{
|
||||
private readonly Version _configuredVersion;
|
||||
private readonly Version _currentVersion;
|
||||
private readonly Version _targetVersion;
|
||||
private readonly string _productName;
|
||||
|
||||
public MigrationRunner(Version configuredVersion, Version targetVersion, string productName)
|
||||
public MigrationRunner(Version currentVersion, Version targetVersion, string productName)
|
||||
{
|
||||
_configuredVersion = configuredVersion;
|
||||
_currentVersion = currentVersion;
|
||||
_targetVersion = targetVersion;
|
||||
_productName = productName;
|
||||
}
|
||||
@@ -46,20 +46,20 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
{
|
||||
LogHelper.Info<MigrationRunner>("Initializing database migrations");
|
||||
|
||||
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
|
||||
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
|
||||
|
||||
//filter all non-schema migrations
|
||||
var migrations = isUpgrade
|
||||
? OrderedUpgradeMigrations(foundMigrations).ToList()
|
||||
: OrderedDowngradeMigrations(foundMigrations).ToList();
|
||||
|
||||
|
||||
//SD: Why do we want this?
|
||||
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _targetVersion, true), this))
|
||||
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
|
||||
return false;
|
||||
|
||||
//Loop through migrations to generate sql
|
||||
var migrationContext = InitializeMigrations(migrations, database, databaseProvider, isUpgrade);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
ExecuteMigrations(migrationContext, database);
|
||||
@@ -81,13 +81,13 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
throw;
|
||||
}
|
||||
|
||||
Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _configuredVersion, _targetVersion, false), this);
|
||||
Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ExecuteMigrations(IMigrationContext context, Database database)
|
||||
{
|
||||
private void ExecuteMigrations(IMigrationContext context, Database database)
|
||||
{
|
||||
//Transactional execution of the sql that was generated from the found migrations
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
@@ -108,13 +108,13 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
|
||||
transaction.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal MigrationContext InitializeMigrations(List<IMigration> migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
|
||||
{
|
||||
{
|
||||
//Loop through migrations to generate sql
|
||||
var context = new MigrationContext(databaseProvider, database);
|
||||
|
||||
|
||||
foreach (var migration in migrations)
|
||||
{
|
||||
var baseMigration = migration as MigrationBase;
|
||||
@@ -148,35 +148,49 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var migrations = (from migration in foundMigrations
|
||||
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
|
||||
from migrationAttribute in migrationAttributes
|
||||
where migrationAttribute != null
|
||||
where
|
||||
migrationAttribute.TargetVersion > _configuredVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
|
||||
select migration).Distinct();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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 > _configuredVersion &&
|
||||
migrationAttribute.TargetVersion <= _targetVersion &&
|
||||
migrationAttribute.ProductName == _productName
|
||||
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
|
||||
select migration).Distinct();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+2
-9
@@ -5,6 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
|
||||
{
|
||||
|
||||
//see: http://issues.umbraco.org/issue/U4-4430
|
||||
[Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AssignMissingPrimaryForMySqlKeys : MigrationBase
|
||||
@@ -14,15 +15,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
|
||||
|
||||
//This should be 2 because this table has 2 keys
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsContentType2ContentType")
|
||||
.OnTable("cmsContentType2ContentType")
|
||||
.Columns(new[] {"parentContentTypeId", "childContentTypeId"});
|
||||
}
|
||||
|
||||
|
||||
//This should be 2 because this table has 2 keys
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentTypeAllowedContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
|
||||
{
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
|
||||
{
|
||||
//We have to target this specifically to ensure this DOES NOT execute if upgrading from a version previous to 6.0,
|
||||
// this is because when the 6.0.0 migrations are executed, this primary key get's created so if this migration is also executed
|
||||
// we will get exceptions because it is trying to create the PK two times.
|
||||
[Migration("6.0.0", "6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class AssignMissingPrimaryForMySqlKeys2 : MigrationBase
|
||||
{
|
||||
public override void Up()
|
||||
{
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
|
||||
|
||||
//This should be 2 because this table has 2 keys
|
||||
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
|
||||
{
|
||||
Create.PrimaryKey("PK_cmsContentType2ContentType")
|
||||
.OnTable("cmsContentType2ContentType")
|
||||
.Columns(new[] {"parentContentTypeId", "childContentTypeId"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
//don't do anything, these keys should have always existed!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Private class to handle preview insert/update based on standard principles and units of work with transactions
|
||||
/// </summary>
|
||||
internal class ContentPreviewRepository<TContent> : PetaPocoRepositoryBase<int, ContentPreviewEntity<TContent>>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentPreviewRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
|
||||
: base(work, cache)
|
||||
{
|
||||
}
|
||||
|
||||
#region Not implemented (don't need to for the purposes of this repo)
|
||||
protected override ContentPreviewEntity<TContent> PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentPreviewEntity<TContent>> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentPreviewEntity<TContent>> PerformGetByQuery(IQuery<ContentPreviewEntity<TContent>> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
protected override void PersistDeletedItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override void PersistNewItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot insert a preview for a content item that has no identity");
|
||||
}
|
||||
|
||||
var previewPoco = new PreviewXmlDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Timestamp = DateTime.Now,
|
||||
VersionId = entity.Version,
|
||||
Xml = entity.Xml.ToString(SaveOptions.None)
|
||||
};
|
||||
|
||||
Database.Insert(previewPoco);
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(ContentPreviewEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update a preview for a content item that has no identity");
|
||||
}
|
||||
|
||||
var previewPoco = new PreviewXmlDto
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Timestamp = DateTime.Now,
|
||||
VersionId = entity.Version,
|
||||
Xml = entity.Xml.ToString(SaveOptions.None)
|
||||
};
|
||||
|
||||
Database.Update<PreviewXmlDto>(
|
||||
"SET xml = @Xml, timestamp = @Timestamp WHERE nodeId = @Id AND versionId = @Version",
|
||||
new
|
||||
{
|
||||
Xml = previewPoco.Xml,
|
||||
Timestamp = previewPoco.Timestamp,
|
||||
Id = previewPoco.NodeId,
|
||||
Version = previewPoco.VersionId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -11,6 +13,7 @@ using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
@@ -23,6 +26,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly ITemplateRepository _templateRepository;
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly ContentPreviewRepository<IContent> _contentPreviewRepository;
|
||||
private readonly ContentXmlRepository<IContent> _contentXmlRepository;
|
||||
|
||||
public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, CacheHelper cacheHelper)
|
||||
: base(work)
|
||||
@@ -30,6 +35,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_templateRepository = templateRepository;
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IContent>(work, NullCacheProvider.Current);
|
||||
_contentXmlRepository = new ContentXmlRepository<IContent>(work, NullCacheProvider.Current);
|
||||
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
@@ -40,6 +47,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_templateRepository = templateRepository;
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IContent>(work, NullCacheProvider.Current);
|
||||
_contentXmlRepository = new ContentXmlRepository<IContent>(work, NullCacheProvider.Current);
|
||||
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
@@ -139,12 +148,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM umbracoRelation WHERE childId = @Id",
|
||||
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoDomains WHERE domainRootStructureID = @Id",
|
||||
"DELETE FROM cmsDocument WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsDocument WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
|
||||
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
|
||||
"DELETE FROM cmsContent WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
@@ -488,11 +497,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IContent> GetByPublishedVersion(IQuery<IContent> query)
|
||||
{
|
||||
// we WANT to return contents in top-down order, ie parents should come before children
|
||||
// ideal would be pure xml "document order" which can be achieved with:
|
||||
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
|
||||
// but that's probably an overkill - sorting by level,sortOrder should be enough
|
||||
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IContent>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.Where<DocumentDto>(x => x.Published)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
|
||||
.OrderBy<NodeDto>(x => x.Level)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
|
||||
//NOTE: This doesn't allow properties to be part of the query
|
||||
@@ -549,6 +563,41 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return repo.GetPermissionsForEntity(entityId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds/updates content/published xml
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
var contentExists = Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @Id", new { Id = content.Id }) != 0;
|
||||
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(contentExists, content, xml));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to remove the content xml for a content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
public void DeleteContentXml(IContent content)
|
||||
{
|
||||
_contentXmlRepository.Delete(new ContentXmlEntity<IContent>(content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds/updates preview xml
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
var previewExists =
|
||||
Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId = @Id AND versionId = @Version",
|
||||
new { Id = content.Id, Version = content.Version }) != 0;
|
||||
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(previewExists, content, xml));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -609,5 +658,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return currentName;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
|
||||
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
|
||||
"DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal class to handle content/published xml insert/update based on standard principles and units of work with transactions
|
||||
/// </summary>
|
||||
internal class ContentXmlRepository<TContent> : PetaPocoRepositoryBase<int, ContentXmlEntity<TContent>>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
public ContentXmlRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
|
||||
: base(work, cache)
|
||||
{
|
||||
}
|
||||
|
||||
#region Not implemented (don't need to for the purposes of this repo)
|
||||
protected override ContentXmlEntity<TContent> PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentXmlEntity<TContent>> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentXmlEntity<TContent>> PerformGetByQuery(IQuery<ContentXmlEntity<TContent>> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void PersistDeletedItem(ContentXmlEntity<TContent> entity)
|
||||
{
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished content
|
||||
Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(ContentXmlEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot insert an xml entry for a content item that has no identity");
|
||||
}
|
||||
|
||||
var poco = new ContentXmlDto { NodeId = entity.Id, Xml = entity.Xml.ToString(SaveOptions.None) };
|
||||
Database.Insert(poco);
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(ContentXmlEntity<TContent> entity)
|
||||
{
|
||||
if (entity.Content.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update an xml entry for a content item that has no identity");
|
||||
}
|
||||
|
||||
var poco = new ContentXmlDto { NodeId = entity.Id, Xml = entity.Xml.ToString(SaveOptions.None) };
|
||||
Database.Update(poco);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
@@ -11,6 +13,8 @@ using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Services;
|
||||
using NullCacheProvider = Umbraco.Core.Persistence.Caching.NullCacheProvider;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
@@ -19,16 +23,30 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DataTypeDefinitionRepository : PetaPocoRepositoryBase<int, IDataTypeDefinition>, IDataTypeDefinitionRepository
|
||||
{
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work)
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly DataTypePreValueRepository _preValRepository;
|
||||
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper,
|
||||
IContentTypeRepository contentTypeRepository)
|
||||
: base(work)
|
||||
{
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_preValRepository = new DataTypePreValueRepository(work, NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, CacheHelper cacheHelper,
|
||||
IContentTypeRepository contentTypeRepository)
|
||||
: base(work, cache)
|
||||
{
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_preValRepository = new DataTypePreValueRepository(work, NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
|
||||
|
||||
#region Overrides of RepositoryBase<int,DataTypeDefinition>
|
||||
|
||||
protected override IDataTypeDefinition PerformGet(int id)
|
||||
@@ -83,6 +101,37 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override the delete method so that we can ensure that all related content type's are updated as part of the overall transaction
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public override void Delete(IDataTypeDefinition entity)
|
||||
{
|
||||
//Find ContentTypes using this IDataTypeDefinition on a PropertyType
|
||||
var query = Query<PropertyType>.Builder.Where(x => x.DataTypeDefinitionId == entity.Id);
|
||||
var contentTypes = _contentTypeRepository.GetByQuery(query);
|
||||
|
||||
//Loop through the list of results and remove the PropertyTypes that references the DataTypeDefinition that is being deleted
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
if (contentType == null) continue;
|
||||
|
||||
foreach (var group in contentType.PropertyGroups)
|
||||
{
|
||||
var types = @group.PropertyTypes.Where(x => x.DataTypeDefinitionId == entity.Id).ToList();
|
||||
foreach (var propertyType in types)
|
||||
{
|
||||
@group.PropertyTypes.Remove(propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
_contentTypeRepository.AddOrUpdate(contentType);
|
||||
}
|
||||
|
||||
//call the base method to queue the deletion of this data type
|
||||
base.Delete(entity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int,DataTypeDefinition>
|
||||
@@ -238,5 +287,322 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
|
||||
{
|
||||
using (var l = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
return cached.First();
|
||||
}
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
|
||||
return GetAndCachePreValueCollection(dataTypeId);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPreValueAsString(int preValueId)
|
||||
{
|
||||
using (var l = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
//We need to see if we can find the cached PreValueCollection based on the cache key above
|
||||
|
||||
var regex = CacheKeys.DataTypePreValuesCacheKey + @"[\d]+-[,\d]*" + preValueId + @"[,\d$]*";
|
||||
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(regex);
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
var collection = cached.First();
|
||||
var preVal = collection.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
|
||||
return preVal.Value.Value;
|
||||
}
|
||||
|
||||
l.UpgradeToWriteLock();
|
||||
|
||||
//go and find the data type id for the pre val id passed in
|
||||
|
||||
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId = preValueId });
|
||||
if (dto == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
// go cache the collection
|
||||
var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId);
|
||||
|
||||
//return the single value for this id
|
||||
var pv = preVals.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
|
||||
return pv.Value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddOrUpdatePreValues(int dataTypeId, IDictionary<string, PreValue> values)
|
||||
{
|
||||
var dtd = Get(dataTypeId);
|
||||
if (dtd == null)
|
||||
{
|
||||
throw new InvalidOperationException("No data type found with id " + dataTypeId);
|
||||
}
|
||||
AddOrUpdatePreValues(dtd, values);
|
||||
}
|
||||
|
||||
public void AddOrUpdatePreValues(IDataTypeDefinition dataType, IDictionary<string, PreValue> values)
|
||||
{
|
||||
var currentVals = new DataTypePreValueDto[]{};
|
||||
if (dataType.HasIdentity)
|
||||
{
|
||||
//first just get all pre-values for this data type so we can compare them to see if we need to insert or update or replace
|
||||
var sql = new Sql().Select("*")
|
||||
.From<DataTypePreValueDto>()
|
||||
.Where<DataTypePreValueDto>(dto => dto.DataTypeNodeId == dataType.Id)
|
||||
.OrderBy<DataTypePreValueDto>(dto => dto.SortOrder);
|
||||
currentVals = Database.Fetch<DataTypePreValueDto>(sql).ToArray();
|
||||
}
|
||||
|
||||
//already existing, need to be updated
|
||||
var valueIds = values.Where(x => x.Value.Id > 0).Select(x => x.Value.Id).ToArray();
|
||||
var existingByIds = currentVals.Where(x => valueIds.Contains(x.Id)).ToArray();
|
||||
|
||||
//These ones need to be removed from the db, they no longer exist in the new values
|
||||
var deleteById = currentVals.Where(x => valueIds.Contains(x.Id) == false);
|
||||
|
||||
foreach (var d in deleteById)
|
||||
{
|
||||
_preValRepository.Delete(new PreValueEntity
|
||||
{
|
||||
Alias = d.Alias,
|
||||
Id = d.Id,
|
||||
Value = d.Value,
|
||||
DataType = dataType,
|
||||
SortOrder = d.SortOrder
|
||||
});
|
||||
}
|
||||
|
||||
var sortOrder = 1;
|
||||
|
||||
foreach (var pre in values)
|
||||
{
|
||||
var existing = existingByIds.FirstOrDefault(valueDto => valueDto.Id == pre.Value.Id);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Value = pre.Value.Value;
|
||||
existing.SortOrder = sortOrder;
|
||||
_preValRepository.AddOrUpdate(new PreValueEntity
|
||||
{
|
||||
//setting an id will update it
|
||||
Id = existing.Id,
|
||||
Alias = existing.Alias,
|
||||
SortOrder = existing.SortOrder,
|
||||
Value = existing.Value,
|
||||
DataType = dataType,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_preValRepository.AddOrUpdate(new PreValueEntity
|
||||
{
|
||||
Alias = pre.Key,
|
||||
SortOrder = sortOrder,
|
||||
Value = pre.Value.Value,
|
||||
DataType = dataType,
|
||||
});
|
||||
}
|
||||
|
||||
sortOrder++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private string GetPrefixedCacheKey(int dataTypeId)
|
||||
{
|
||||
return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-";
|
||||
}
|
||||
|
||||
private PreValueCollection GetAndCachePreValueCollection(int dataTypeId)
|
||||
{
|
||||
//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.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
|
||||
//the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS
|
||||
|
||||
var key = GetPrefixedCacheKey(dataTypeId)
|
||||
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
|
||||
|
||||
//store into cache
|
||||
_cacheHelper.RuntimeCache.InsertCacheItem(key, () => collection,
|
||||
//30 mins
|
||||
new TimeSpan(0, 0, 30),
|
||||
//sliding is true
|
||||
true);
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private class to handle pre-value crud based on units of work with transactions
|
||||
/// </summary>
|
||||
private class PreValueEntity : Entity, IAggregateRoot
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public IDataTypeDefinition DataType { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private class to handle pre-value crud based on standard principles and units of work with transactions
|
||||
/// </summary>
|
||||
private class DataTypePreValueRepository : PetaPocoRepositoryBase<int, PreValueEntity>
|
||||
{
|
||||
public DataTypePreValueRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
|
||||
{
|
||||
}
|
||||
|
||||
#region Not implemented (don't need to for the purposes of this repo)
|
||||
protected override PreValueEntity PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<PreValueEntity> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<PreValueEntity> PerformGetByQuery(IQuery<PreValueEntity> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override void PersistDeletedItem(PreValueEntity entity)
|
||||
{
|
||||
Database.Execute(
|
||||
"DELETE FROM cmsDataTypePreValues WHERE id=@Id",
|
||||
new { Id = entity.Id });
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(PreValueEntity entity)
|
||||
{
|
||||
if (entity.DataType.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot insert a pre value for a data type that has no identity");
|
||||
}
|
||||
|
||||
//Cannot add a duplicate alias
|
||||
var exists = Database.ExecuteScalar<int>(@"SELECT COUNT(*) FROM cmsDataTypePreValues
|
||||
WHERE alias = @alias
|
||||
AND datatypeNodeId = @dtdid",
|
||||
new { alias = entity.Alias, dtdid = entity.DataType.Id });
|
||||
if (exists > 0)
|
||||
{
|
||||
throw new DuplicateNameException("A pre value with the alias " + entity.Alias + " already exists for this data type");
|
||||
}
|
||||
|
||||
var dto = new DataTypePreValueDto
|
||||
{
|
||||
DataTypeNodeId = entity.DataType.Id,
|
||||
Value = entity.Value,
|
||||
SortOrder = entity.SortOrder,
|
||||
Alias = entity.Alias
|
||||
};
|
||||
Database.Insert(dto);
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(PreValueEntity entity)
|
||||
{
|
||||
if (entity.DataType.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity");
|
||||
}
|
||||
|
||||
//Cannot change to a duplicate alias
|
||||
var exists = Database.ExecuteScalar<int>(@"SELECT COUNT(*) FROM cmsDataTypePreValues
|
||||
WHERE alias = @alias
|
||||
AND datatypeNodeId = @dtdid
|
||||
AND id <> @id",
|
||||
new { id = entity.Id, alias = entity.Alias, dtdid = entity.DataType.Id });
|
||||
if (exists > 0)
|
||||
{
|
||||
throw new DuplicateNameException("A pre value with the alias " + entity.Alias + " already exists for this data type");
|
||||
}
|
||||
|
||||
var dto = new DataTypePreValueDto
|
||||
{
|
||||
DataTypeNodeId = entity.DataType.Id,
|
||||
Id = entity.Id,
|
||||
Value = entity.Value,
|
||||
SortOrder = entity.SortOrder,
|
||||
Alias = entity.Alias
|
||||
};
|
||||
Database.Update(dto);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class PreValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the tuple to a pre-value collection
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
internal static PreValueCollection ConvertToPreValuesCollection(IEnumerable<Tuple<PreValue, string, int>> list)
|
||||
{
|
||||
//now we need to determine if they are dictionary based, otherwise they have to be array based
|
||||
var dictionary = new Dictionary<string, PreValue>();
|
||||
|
||||
//need to check all of the keys, if there's only one and it is empty then it's an array
|
||||
var keys = list.Select(x => x.Item2).Distinct().ToArray();
|
||||
if (keys.Length == 1 && keys[0].IsNullOrWhiteSpace())
|
||||
{
|
||||
return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1));
|
||||
}
|
||||
|
||||
foreach (var item in list
|
||||
.OrderBy(x => x.Item3) //we'll order them first so we maintain the order index in the dictionary
|
||||
.GroupBy(x => x.Item2)) //group by alias
|
||||
{
|
||||
if (item.Count() > 1)
|
||||
{
|
||||
//if there's more than 1 item per key, then it cannot be a dictionary, just return the array
|
||||
return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1));
|
||||
}
|
||||
|
||||
dictionary.Add(item.Key, item.First().Item1);
|
||||
}
|
||||
|
||||
return new PreValueCollection(dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -37,5 +38,26 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entityId"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<EntityPermission> GetPermissionsForEntity(int entityId);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update published xml for the content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml);
|
||||
|
||||
/// <summary>
|
||||
/// Used to remove the content xml for a content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
void DeleteContentXml(IContent content);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update preview xml for the content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml);
|
||||
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,9 +1,15 @@
|
||||
using Umbraco.Core.Models;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IDataTypeDefinitionRepository : IRepositoryQueryable<int, IDataTypeDefinition>
|
||||
{
|
||||
|
||||
PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId);
|
||||
string GetPreValueAsString(int preValueId);
|
||||
|
||||
void AddOrUpdatePreValues(IDataTypeDefinition dataType, IDictionary<string, PreValue> values);
|
||||
void AddOrUpdatePreValues(int dataTypeId, IDictionary<string, PreValue> values);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,25 @@
|
||||
using Umbraco.Core.Models;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update published xml for the media item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdateContentXml(IMedia content, Func<IMedia, XElement> xml);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update preview xml for the content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdatePreviewXml(IMedia content, Func<IMedia, XElement> xml);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -54,5 +55,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Sql sql, int pageIndex, int pageSize, out int totalRecords,
|
||||
Func<IEnumerable<TDto>, int[]> resolveIds);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update published xml for the media item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdateContentXml(IMember content, Func<IMember, XElement> xml);
|
||||
|
||||
/// <summary>
|
||||
/// Used to add/update preview xml for the content item
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -20,12 +21,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
internal class MediaRepository : VersionableRepositoryBase<int, IMedia>, IMediaRepository
|
||||
{
|
||||
private readonly IMediaTypeRepository _mediaTypeRepository;
|
||||
private readonly ContentXmlRepository<IMedia> _contentXmlRepository;
|
||||
private readonly ContentPreviewRepository<IMedia> _contentPreviewRepository;
|
||||
|
||||
public MediaRepository(IDatabaseUnitOfWork work, IMediaTypeRepository mediaTypeRepository)
|
||||
: base(work)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository;
|
||||
|
||||
_contentXmlRepository = new ContentXmlRepository<IMedia>(work, NullCacheProvider.Current);
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IMedia>(work, NullCacheProvider.Current);
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
@@ -33,7 +37,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
: base(work, cache)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository;
|
||||
|
||||
_contentXmlRepository = new ContentXmlRepository<IMedia>(work, NullCacheProvider.Current);
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IMedia>(work, NullCacheProvider.Current);
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
@@ -130,12 +135,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM umbracoRelation WHERE parentId = @Id",
|
||||
"DELETE FROM umbracoRelation WHERE childId = @Id",
|
||||
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsDocument WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsDocument WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
|
||||
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
|
||||
"DELETE FROM cmsContent WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
@@ -174,6 +179,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return media;
|
||||
}
|
||||
|
||||
public void AddOrUpdateContentXml(IMedia content, Func<IMedia, XElement> xml)
|
||||
{
|
||||
var contentExists = Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @Id", new { Id = content.Id }) != 0;
|
||||
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMedia>(contentExists, content, xml));
|
||||
}
|
||||
|
||||
public void AddOrUpdatePreviewXml(IMedia content, Func<IMedia, XElement> xml)
|
||||
{
|
||||
var previewExists =
|
||||
Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId = @Id AND versionId = @Version",
|
||||
new { Id = content.Id, Version = content.Version }) != 0;
|
||||
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMedia>(previewExists, content, xml));
|
||||
}
|
||||
|
||||
protected override void PerformDeleteVersion(int id, Guid versionId)
|
||||
{
|
||||
Database.Delete<PreviewXmlDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id",
|
||||
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
|
||||
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
|
||||
@@ -120,7 +120,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
group.AddingEntity();
|
||||
var dto = _modelFactory.BuildDto(group);
|
||||
var o = Database.IsNew(dto) ? Convert.ToInt32(Database.Insert(dto)) : Database.Update(dto);
|
||||
|
||||
group.Id = dto.NodeId; //Set Id on entity to ensure an Id is set
|
||||
|
||||
//Update with new correct path
|
||||
dto.Path = string.Concat("-1,", dto.NodeId);
|
||||
Database.Update(dto);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -24,6 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
private readonly IMemberTypeRepository _memberTypeRepository;
|
||||
private readonly IMemberGroupRepository _memberGroupRepository;
|
||||
private readonly ContentXmlRepository<IMember> _contentXmlRepository;
|
||||
private readonly ContentPreviewRepository<IMember> _contentPreviewRepository;
|
||||
|
||||
public MemberRepository(IDatabaseUnitOfWork work, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository)
|
||||
: base(work)
|
||||
@@ -31,6 +34,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository");
|
||||
_memberTypeRepository = memberTypeRepository;
|
||||
_memberGroupRepository = memberGroupRepository;
|
||||
_contentXmlRepository = new ContentXmlRepository<IMember>(work, NullCacheProvider.Current);
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IMember>(work, NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
public MemberRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository)
|
||||
@@ -39,6 +44,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository");
|
||||
_memberTypeRepository = memberTypeRepository;
|
||||
_memberGroupRepository = memberGroupRepository;
|
||||
_contentXmlRepository = new ContentXmlRepository<IMember>(work, NullCacheProvider.Current);
|
||||
_contentPreviewRepository = new ContentPreviewRepository<IMember>(work, NullCacheProvider.Current);
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int, IMembershipUser>
|
||||
@@ -177,8 +184,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsMember2MemberGroup WHERE Member = @Id",
|
||||
"DELETE FROM cmsMember WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
|
||||
"DELETE FROM cmsContent WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
@@ -323,7 +330,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
changedCols.Add("LoginName");
|
||||
}
|
||||
// DO NOT update the password if it is null or empty
|
||||
// DO NOT update the password if it has not changed or if it is null or empty
|
||||
if (dirtyEntity.IsPropertyDirty("RawPasswordValue") && entity.RawPasswordValue.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changedCols.Add("Password");
|
||||
@@ -594,6 +601,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return GetAll(resolveIds(pagedResult.Items)).ToArray();
|
||||
}
|
||||
|
||||
public void AddOrUpdateContentXml(IMember content, Func<IMember, XElement> xml)
|
||||
{
|
||||
var contentExists = Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @Id", new { Id = content.Id }) != 0;
|
||||
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMember>(contentExists, content, xml));
|
||||
}
|
||||
|
||||
public void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml)
|
||||
{
|
||||
var previewExists =
|
||||
Database.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId = @Id AND versionId = @Version",
|
||||
new { Id = content.Id, Version = content.Version }) != 0;
|
||||
|
||||
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMember>(previewExists, content, xml));
|
||||
}
|
||||
|
||||
private IMember BuildFromDto(List<MemberReadOnlyDto> dtos)
|
||||
{
|
||||
if (dtos == null || dtos.Any() == false)
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
|
||||
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
|
||||
"DELETE FROM cmsMemberType WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE NodeId = @Id",
|
||||
"DELETE FROM cmsContentType WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
|
||||
@@ -86,12 +86,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
FormatDeleteStatement("umbracoRelation", "childId"),
|
||||
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
|
||||
FormatDeleteStatement("umbracoDomains", "domainRootStructureID"),
|
||||
FormatDeleteStatement("cmsDocument", "NodeId"),
|
||||
FormatDeleteStatement("cmsDocument", "nodeId"),
|
||||
FormatDeleteStatement("cmsPropertyData", "contentNodeId"),
|
||||
FormatDeleteStatement("cmsPreviewXml", "nodeId"),
|
||||
FormatDeleteStatement("cmsContentVersion", "ContentId"),
|
||||
FormatDeleteStatement("cmsContentXml", "nodeID"),
|
||||
FormatDeleteStatement("cmsContent", "NodeId"),
|
||||
FormatDeleteStatement("cmsContentXml", "nodeId"),
|
||||
FormatDeleteStatement("cmsContent", "nodeId"),
|
||||
"UPDATE umbracoNode SET parentID = '-20' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
|
||||
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
|
||||
};
|
||||
@@ -131,18 +131,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
Parallel.ForEach(files, file =>
|
||||
{
|
||||
if (UmbracoSettings.UploadAllowDirectories)
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(file);
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
fs.DeleteDirectory(parentDirectory, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.DeleteFile(file, true);
|
||||
}
|
||||
});
|
||||
{
|
||||
if (file.IsNullOrWhiteSpace()) return;
|
||||
if (UmbracoSettings.UploadAllowDirectories)
|
||||
{
|
||||
var relativeFilePath = fs.GetRelativePath(file);
|
||||
var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath);
|
||||
fs.DeleteDirectory(parentDirectory, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.DeleteFile(file, true);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// Deletes the passed in entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public void Delete(TEntity entity)
|
||||
public virtual void Delete(TEntity entity)
|
||||
{
|
||||
if(_work != null)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -135,7 +136,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsTask WHERE parentUserId = @Id",
|
||||
"DELETE FROM umbracoUser2NodePermission WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUserLogins WHERE userId = @Id",
|
||||
"DELETE FROM umbracoUserLogins WHERE userID = @Id",
|
||||
"DELETE FROM umbracoUser2app WHERE " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@Id",
|
||||
"DELETE FROM umbracoUser WHERE id = @Id"
|
||||
};
|
||||
@@ -170,8 +171,42 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var userFactory = new UserFactory(entity.UserType);
|
||||
var userDto = userFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(userDto);
|
||||
var dirtyEntity = (ICanBeDirty)entity;
|
||||
|
||||
//build list of columns to check for saving - we don't want to save the password if it hasn't changed!
|
||||
//List the columns to save, NOTE: would be nice to not have hard coded strings here but no real good way around that
|
||||
var colsToSave = new Dictionary<string, string>()
|
||||
{
|
||||
{"userDisabled", "IsApproved"},
|
||||
{"userNoConsole", "IsLockedOut"},
|
||||
{"userType", "UserType"},
|
||||
{"startStructureID", "StartContentId"},
|
||||
{"startMediaID", "StartMediaId"},
|
||||
{"userName", "Name"},
|
||||
{"userLogin", "Username"},
|
||||
{"userEmail", "Email"},
|
||||
{"userLanguage", "Language"},
|
||||
{"defaultToLiveEditing", "DefaultToLiveEditing"}
|
||||
};
|
||||
|
||||
//create list of properties that have changed
|
||||
var changedCols = colsToSave
|
||||
.Where(col => dirtyEntity.IsPropertyDirty(col.Value))
|
||||
.Select(col => col.Key)
|
||||
.ToList();
|
||||
|
||||
// DO NOT update the password if it has not changed or if it is null or empty
|
||||
if (dirtyEntity.IsPropertyDirty("RawPasswordValue") && entity.RawPasswordValue.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changedCols.Add("userPassword");
|
||||
}
|
||||
|
||||
//only update the changed cols
|
||||
if (changedCols.Count > 0)
|
||||
{
|
||||
Database.Update(userDto, changedCols);
|
||||
}
|
||||
|
||||
//update the sections if they've changed
|
||||
var user = (User)entity;
|
||||
if (user.IsPropertyDirty("AllowedSections"))
|
||||
|
||||
@@ -55,14 +55,16 @@ namespace Umbraco.Core.Persistence
|
||||
return new ContentTypeRepository(
|
||||
uow,
|
||||
_disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current,
|
||||
new TemplateRepository(uow, NullCacheProvider.Current));
|
||||
CreateTemplateRepository(uow));
|
||||
}
|
||||
|
||||
public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new DataTypeDefinitionRepository(
|
||||
uow,
|
||||
NullCacheProvider.Current);
|
||||
_disableAllCache ? (IRepositoryCacheProvider)NullCacheProvider.Current : RuntimeCacheProvider.Current,
|
||||
_cacheHelper,
|
||||
CreateContentTypeRepository(uow));
|
||||
}
|
||||
|
||||
public virtual IDictionaryRepository CreateDictionaryRepository(IDatabaseUnitOfWork uow)
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace Umbraco.Core.Security
|
||||
/// <remarks>
|
||||
/// Checks to ensure the AllowManuallyChangingPassword rule is adhered to
|
||||
/// </remarks>
|
||||
public sealed override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
public override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false)
|
||||
{
|
||||
@@ -388,7 +388,7 @@ namespace Umbraco.Core.Security
|
||||
/// <remarks>
|
||||
/// Ensures the ValidatingPassword event is executed before executing PerformCreateUser and performs basic membership provider validation of values.
|
||||
/// </remarks>
|
||||
public sealed override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
var valStatus = ValidateNewUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey);
|
||||
if (valStatus != MembershipCreateStatus.Success)
|
||||
@@ -476,7 +476,7 @@ namespace Umbraco.Core.Security
|
||||
/// <param name="username"></param>
|
||||
/// <param name="answer"></param>
|
||||
/// <returns></returns>
|
||||
public sealed override string GetPassword(string username, string answer)
|
||||
public override string GetPassword(string username, string answer)
|
||||
{
|
||||
if (EnablePasswordRetrieval == false)
|
||||
throw new ProviderException("Password Retrieval Not Enabled.");
|
||||
@@ -495,7 +495,7 @@ namespace Umbraco.Core.Security
|
||||
/// <returns></returns>
|
||||
protected abstract string PerformGetPassword(string username, string answer);
|
||||
|
||||
public sealed override string ResetPassword(string username, string answer)
|
||||
public override string ResetPassword(string username, string answer)
|
||||
{
|
||||
if (EnablePasswordReset == false)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,9 @@ namespace Umbraco.Core.Security
|
||||
public static MembershipUser GetCurrentUser(this MembershipProvider membershipProvider)
|
||||
{
|
||||
var username = membershipProvider.GetCurrentUserName();
|
||||
return membershipProvider.GetUser(username, true);
|
||||
return username.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: membershipProvider.GetUser(username, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,7 +80,7 @@ namespace Umbraco.Core.Security
|
||||
if (HostingEnvironment.IsHosted)
|
||||
{
|
||||
HttpContext current = HttpContext.Current;
|
||||
if (current != null)
|
||||
if (current != null && current.User != null && current.User.Identity != null)
|
||||
return current.User.Identity.Name;
|
||||
}
|
||||
IPrincipal currentPrincipal = Thread.CurrentPrincipal;
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace Umbraco.Core.Services
|
||||
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
|
||||
private readonly IPublishingStrategy _publishingStrategy;
|
||||
private readonly RepositoryFactory _repositoryFactory;
|
||||
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
//Support recursive locks because some of the methods that require locking call other methods that require locking.
|
||||
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
@@ -57,6 +60,18 @@ namespace Umbraco.Core.Services
|
||||
_uowProvider = provider;
|
||||
_publishingStrategy = publishingStrategy;
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_dataTypeService = new DataTypeService(provider, repositoryFactory);
|
||||
}
|
||||
|
||||
public ContentService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IPublishingStrategy publishingStrategy, IDataTypeService dataTypeService)
|
||||
{
|
||||
if (provider == null) throw new ArgumentNullException("provider");
|
||||
if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory");
|
||||
if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy");
|
||||
_uowProvider = provider;
|
||||
_publishingStrategy = publishingStrategy;
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -196,6 +211,8 @@ namespace Umbraco.Core.Services
|
||||
content.CreatorId = userId;
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
//Generate a new preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
@@ -246,6 +263,8 @@ namespace Umbraco.Core.Services
|
||||
content.CreatorId = userId;
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
//Generate a new preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
@@ -770,21 +789,23 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
|
||||
public void Save(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = contents.ToArray();
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IContent>(contents), this))
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IContent>(asArray), this))
|
||||
return;
|
||||
}
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var containsNew = contents.Any(x => x.HasIdentity == false);
|
||||
var containsNew = asArray.Any(x => x.HasIdentity == false);
|
||||
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
if (containsNew)
|
||||
{
|
||||
foreach (var content in contents)
|
||||
foreach (var content in asArray)
|
||||
{
|
||||
content.WriterId = userId;
|
||||
|
||||
@@ -793,22 +814,26 @@ namespace Umbraco.Core.Services
|
||||
content.ChangePublishedState(PublishedState.Saved);
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
uow.Commit();
|
||||
//add or update preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var content in contents)
|
||||
foreach (var content in asArray)
|
||||
{
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
}
|
||||
uow.Commit();
|
||||
//add or update preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
Saved.RaiseEvent(new SaveEventArgs<IContent>(contents, false), this);
|
||||
Saved.RaiseEvent(new SaveEventArgs<IContent>(asArray, false), this);
|
||||
|
||||
Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1);
|
||||
}
|
||||
@@ -968,8 +993,19 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
if (Trashing.IsRaisedEventCancelled(new MoveEventArgs<IContent>(content, -20), this))
|
||||
var originalPath = content.Path;
|
||||
|
||||
if (Trashing.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IContent>(
|
||||
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)), this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var moveInfo = new List<MoveEventInfo<IContent>>
|
||||
{
|
||||
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)
|
||||
};
|
||||
|
||||
//Make sure that published content is unpublished before being moved to the Recycle Bin
|
||||
if (HasPublishedVersion(content.Id))
|
||||
@@ -994,6 +1030,8 @@ namespace Umbraco.Core.Services
|
||||
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
|
||||
foreach (var descendant in descendants)
|
||||
{
|
||||
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
|
||||
|
||||
descendant.WriterId = userId;
|
||||
descendant.ChangeTrashedState(true, descendant.ParentId);
|
||||
repository.AddOrUpdate(descendant);
|
||||
@@ -1002,7 +1040,7 @@ namespace Umbraco.Core.Services
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Trashed.RaiseEvent(new MoveEventArgs<IContent>(content, false, -20), this);
|
||||
Trashed.RaiseEvent(new MoveEventArgs<IContent>(false, moveInfo.ToArray()), this);
|
||||
|
||||
Audit.Add(AuditTypes.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
|
||||
}
|
||||
@@ -1029,76 +1067,21 @@ namespace Umbraco.Core.Services
|
||||
MoveToRecycleBin(content, userId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Moving.IsRaisedEventCancelled(new MoveEventArgs<IContent>(content, parentId), this))
|
||||
|
||||
if (Moving.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IContent>(
|
||||
new MoveEventInfo<IContent>(content, content.Path, parentId)), this))
|
||||
{
|
||||
return;
|
||||
|
||||
content.WriterId = userId;
|
||||
if (parentId == -1)
|
||||
{
|
||||
content.Path = string.Concat("-1,", content.Id);
|
||||
content.Level = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var parent = GetById(parentId);
|
||||
content.Path = string.Concat(parent.Path, ",", content.Id);
|
||||
content.Level = parent.Level + 1;
|
||||
}
|
||||
|
||||
//used to track all the moved entities to be given to the event
|
||||
var moveInfo = new List<MoveEventInfo<IContent>>();
|
||||
|
||||
//If Content is being moved away from Recycle Bin, its state should be un-trashed
|
||||
if (content.Trashed && parentId != -20)
|
||||
{
|
||||
content.ChangeTrashedState(false, parentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
content.ParentId = parentId;
|
||||
}
|
||||
//call private method that does the recursive moving
|
||||
PerformMove(content, parentId, userId, moveInfo);
|
||||
|
||||
//If Content is published, it should be (re)published from its new location
|
||||
if (content.Published)
|
||||
{
|
||||
//If Content is Publishable its saved and published
|
||||
//otherwise we save the content without changing the publish state, and generate new xml because the Path, Level and Parent has changed.
|
||||
if (IsPublishable(content))
|
||||
{
|
||||
SaveAndPublish(content, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Save(content, false, userId, true);
|
||||
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
var xml = content.ToXml();
|
||||
var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var exists =
|
||||
uow.Database.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = content.Id }) !=
|
||||
null;
|
||||
int result = exists
|
||||
? uow.Database.Update(poco)
|
||||
: Convert.ToInt32(uow.Database.Insert(poco));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Save(content, userId);
|
||||
}
|
||||
|
||||
//Ensure that Path and Level is updated on children
|
||||
var children = GetChildren(content.Id).ToArray();
|
||||
if (children.Any())
|
||||
{
|
||||
foreach (var child in children)
|
||||
{
|
||||
Move(child, content.Id, userId);
|
||||
}
|
||||
}
|
||||
|
||||
Moved.RaiseEvent(new MoveEventArgs<IContent>(content, false, parentId), this);
|
||||
Moved.RaiseEvent(new MoveEventArgs<IContent>(false, moveInfo.ToArray()), this);
|
||||
|
||||
Audit.Add(AuditTypes.Move, "Move Content performed by user", userId, content.Id);
|
||||
}
|
||||
@@ -1145,9 +1128,12 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>The newly created <see cref="IContent"/> object</returns>
|
||||
public IContent Copy(IContent content, int parentId, bool relateToOriginal, int userId = 0)
|
||||
{
|
||||
//TODO: This all needs to be managed correctly so that the logic is submitted in one
|
||||
// transaction, the CRUD needs to be moved to the repo
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var copy = ((Content)content).Clone();
|
||||
var copy = content.DeepCloneWithResetIdentities();
|
||||
copy.ParentId = parentId;
|
||||
|
||||
// A copy should never be set to published automatically even if the original was.
|
||||
@@ -1164,9 +1150,12 @@ namespace Umbraco.Core.Services
|
||||
copy.WriterId = userId;
|
||||
|
||||
repository.AddOrUpdate(copy);
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
|
||||
//Special case for the Upload DataType
|
||||
//TODO: This really shouldn't be here! What about the cropper in v7, we'll have the same issue.
|
||||
var uploadDataTypeId = new Guid(Constants.PropertyEditors.UploadField);
|
||||
if (content.Properties.Any(x => x.PropertyType.DataTypeId == uploadDataTypeId))
|
||||
{
|
||||
@@ -1199,10 +1188,13 @@ namespace Umbraco.Core.Services
|
||||
if (isUpdated)
|
||||
{
|
||||
repository.AddOrUpdate(copy);
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Move this to the repository layer in a single transaction!
|
||||
//Special case for the Tags DataType
|
||||
var tagsDataTypeId = new Guid(Constants.PropertyEditors.Tags);
|
||||
if (content.Properties.Any(x => x.PropertyType.DataTypeId == tagsDataTypeId))
|
||||
@@ -1216,6 +1208,7 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
|
||||
//NOTE This 'Relation' part should eventually be delegated to a RelationService
|
||||
//TODO: This should be party of a single commit
|
||||
if (relateToOriginal)
|
||||
{
|
||||
IRelationType relationType = null;
|
||||
@@ -1240,14 +1233,15 @@ namespace Umbraco.Core.Services
|
||||
var children = GetChildren(content.Id);
|
||||
foreach (var child in children)
|
||||
{
|
||||
//TODO: This shouldn't recurse back to this method, it should be done in a private method
|
||||
// that doesn't have a nested lock and so we can perform the entire operation in one commit.
|
||||
Copy(child, copy.Id, relateToOriginal, userId);
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -1307,6 +1301,8 @@ namespace Umbraco.Core.Services
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
@@ -1340,13 +1336,14 @@ namespace Umbraco.Core.Services
|
||||
var shouldBePublished = new List<IContent>();
|
||||
var shouldBeSaved = new List<IContent>();
|
||||
|
||||
var asArray = items.ToArray();
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var content in items)
|
||||
foreach (var content in asArray)
|
||||
{
|
||||
//If the current sort order equals that of the content
|
||||
//we don't need to update it, so just increment the sort order
|
||||
@@ -1370,30 +1367,22 @@ namespace Umbraco.Core.Services
|
||||
shouldBeSaved.Add(content);
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
//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));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
|
||||
foreach (var content in shouldBeSaved)
|
||||
{
|
||||
//Create and Save PreviewXml DTO
|
||||
var xml = content.ToXml();
|
||||
CreateAndSavePreviewXml(xml, content.Id, content.Version, uow.Database);
|
||||
}
|
||||
|
||||
foreach (var content in shouldBePublished)
|
||||
{
|
||||
//Create and Save PreviewXml DTO
|
||||
var xml = content.ToXml();
|
||||
CreateAndSavePreviewXml(xml, content.Id, content.Version, uow.Database);
|
||||
//Create and Save ContentXml DTO
|
||||
CreateAndSaveContentXml(xml, content.Id, uow.Database);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
Saved.RaiseEvent(new SaveEventArgs<IContent>(items, false), this);
|
||||
Saved.RaiseEvent(new SaveEventArgs<IContent>(asArray, false), this);
|
||||
|
||||
if (shouldBePublished.Any())
|
||||
_publishingStrategy.PublishingFinalized(shouldBePublished, false);
|
||||
@@ -1461,9 +1450,87 @@ namespace Umbraco.Core.Services
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void PerformMove(IContent content, int parentId, int userId, ICollection<MoveEventInfo<IContent>> moveInfo)
|
||||
{
|
||||
//add a tracking item to use in the Moved event
|
||||
moveInfo.Add(new MoveEventInfo<IContent>(content, content.Path, parentId));
|
||||
|
||||
content.WriterId = userId;
|
||||
if (parentId == -1)
|
||||
{
|
||||
content.Path = string.Concat("-1,", content.Id);
|
||||
content.Level = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var parent = GetById(parentId);
|
||||
content.Path = string.Concat(parent.Path, ",", content.Id);
|
||||
content.Level = parent.Level + 1;
|
||||
}
|
||||
|
||||
//If Content is being moved away from Recycle Bin, its state should be un-trashed
|
||||
if (content.Trashed && parentId != Constants.System.RecycleBinContent)
|
||||
{
|
||||
content.ChangeTrashedState(false, parentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
content.ParentId = parentId;
|
||||
}
|
||||
|
||||
//If Content is published, it should be (re)published from its new location
|
||||
if (content.Published)
|
||||
{
|
||||
//If Content is Publishable its saved and published
|
||||
//otherwise we save the content without changing the publish state, and generate new xml because the Path, Level and Parent has changed.
|
||||
if (IsPublishable(content))
|
||||
{
|
||||
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
|
||||
SaveAndPublish(content, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
|
||||
Save(content, false, userId);
|
||||
|
||||
//TODO: This shouldn't be here! This needs to be part of the repository logic but in order to fix this we need to
|
||||
// change how this method calls "Save" as it needs to save using an internal method
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
var xml = _entitySerializer.Serialize(this, _dataTypeService, content);
|
||||
|
||||
var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var exists =
|
||||
uow.Database.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = content.Id }) !=
|
||||
null;
|
||||
int result = exists
|
||||
? uow.Database.Update(poco)
|
||||
: Convert.ToInt32(uow.Database.Insert(poco));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
|
||||
Save(content, userId);
|
||||
}
|
||||
|
||||
//Ensure that Path and Level is updated on children
|
||||
var children = GetChildren(content.Id).ToArray();
|
||||
if (children.Any())
|
||||
{
|
||||
foreach (var child in children)
|
||||
{
|
||||
PerformMove(child, content.Id, userId, moveInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: WE should make a base class for ContentService and MediaService to share!
|
||||
// currently we have this logic duplicated (nearly the same) for media types and soon to be member types
|
||||
|
||||
//TODO: This needs to be put into the ContentRepository, all CUD logic!
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds all xml content in the cmsContentXml table for all documents
|
||||
/// </summary>
|
||||
@@ -1491,7 +1558,7 @@ namespace Umbraco.Core.Services
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
var xml = c.ToXml();
|
||||
var xml = _entitySerializer.Serialize(this, _dataTypeService, c);
|
||||
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
|
||||
}
|
||||
|
||||
@@ -1608,21 +1675,15 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
item.Result.ContentItem.WriterId = userId;
|
||||
repository.AddOrUpdate(item.Result.ContentItem);
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(item.Result.ContentItem, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
//add or update the published xml
|
||||
repository.AddOrUpdateContentXml(item.Result.ContentItem, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
updated.Add(item.Result.ContentItem);
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
|
||||
foreach (var c in updated)
|
||||
{
|
||||
var xml = c.ToXml();
|
||||
var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var exists = uow.Database.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = c.Id }) !=
|
||||
null;
|
||||
var r = exists
|
||||
? uow.Database.Update(poco)
|
||||
: Convert.ToInt32(uow.Database.Insert(poco));
|
||||
}
|
||||
}
|
||||
//Save xml to db and call following method to fire event:
|
||||
_publishingStrategy.PublishingFinalized(updated, false);
|
||||
@@ -1653,9 +1714,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
content.WriterId = userId;
|
||||
repository.AddOrUpdate(content);
|
||||
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished content
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = content.Id });
|
||||
repository.DeleteContentXml(content);
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
@@ -1728,17 +1787,16 @@ namespace Umbraco.Core.Services
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
|
||||
uow.Commit();
|
||||
|
||||
var xml = content.ToXml();
|
||||
//Preview Xml
|
||||
CreateAndSavePreviewXml(xml, content.Id, content.Version, uow.Database);
|
||||
|
||||
//Generate a new preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
|
||||
if (published)
|
||||
{
|
||||
//Content Xml
|
||||
CreateAndSaveContentXml(xml, content.Id, uow.Database);
|
||||
repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
@@ -1795,11 +1853,11 @@ namespace Umbraco.Core.Services
|
||||
content.ChangePublishedState(PublishedState.Saved);
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
uow.Commit();
|
||||
|
||||
//Preview Xml
|
||||
var xml = content.ToXml();
|
||||
CreateAndSavePreviewXml(xml, content.Id, content.Version, uow.Database);
|
||||
//Generate a new preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c));
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
@@ -1871,39 +1929,7 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
|
||||
return PublishStatusType.Success;
|
||||
}
|
||||
|
||||
private void CreateAndSavePreviewXml(XElement xml, int id, Guid version, UmbracoDatabase db)
|
||||
{
|
||||
var previewPoco = new PreviewXmlDto
|
||||
{
|
||||
NodeId = id,
|
||||
Timestamp = DateTime.Now,
|
||||
VersionId = version,
|
||||
Xml = xml.ToString(SaveOptions.None)
|
||||
};
|
||||
var previewExists =
|
||||
db.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId = @Id AND versionId = @Version",
|
||||
new { Id = id, Version = version }) != 0;
|
||||
int previewResult = previewExists
|
||||
? db.Update<PreviewXmlDto>(
|
||||
"SET xml = @Xml, timestamp = @Timestamp WHERE nodeId = @Id AND versionId = @Version",
|
||||
new
|
||||
{
|
||||
Xml = previewPoco.Xml,
|
||||
Timestamp = previewPoco.Timestamp,
|
||||
Id = previewPoco.NodeId,
|
||||
Version = previewPoco.VersionId
|
||||
})
|
||||
: Convert.ToInt32(db.Insert(previewPoco));
|
||||
}
|
||||
|
||||
private void CreateAndSaveContentXml(XElement xml, int id, UmbracoDatabase db)
|
||||
{
|
||||
var contentPoco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var contentExists = db.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @Id", new { Id = id }) != 0;
|
||||
int contentResult = contentExists ? db.Update(contentPoco) : Convert.ToInt32(db.Insert(contentPoco));
|
||||
}
|
||||
}
|
||||
|
||||
private IContentType FindContentTypeByAlias(string contentTypeAlias)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,89 @@ namespace Umbraco.Core.Services
|
||||
_mediaService = mediaService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
|
||||
/// </summary>
|
||||
/// <param name="original">
|
||||
/// The content type to copy
|
||||
/// </param>
|
||||
/// <param name="alias">
|
||||
/// The new alias of the content type
|
||||
/// </param>
|
||||
/// <param name="name">
|
||||
/// The new name of the content type
|
||||
/// </param>
|
||||
/// <param name="parentId">
|
||||
/// The parent to copy the content type to, default is -1 (root)
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IContentType Copy(IContentType original, string alias, string name, int parentId = -1)
|
||||
{
|
||||
IContentType parent = null;
|
||||
if (parentId > 0)
|
||||
{
|
||||
parent = GetContentType(parentId);
|
||||
if (parent == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find content type with id " + parentId);
|
||||
}
|
||||
}
|
||||
return Copy(original, alias, name, parent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
|
||||
/// </summary>
|
||||
/// <param name="original">
|
||||
/// The content type to copy
|
||||
/// </param>
|
||||
/// <param name="alias">
|
||||
/// The new alias of the content type
|
||||
/// </param>
|
||||
/// <param name="name">
|
||||
/// The new name of the content type
|
||||
/// </param>
|
||||
/// <param name="parent">
|
||||
/// The parent to copy the content type to, default is null (root)
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public IContentType Copy(IContentType original, string alias, string name, IContentType parent)
|
||||
{
|
||||
Mandate.ParameterNotNull(original, "original");
|
||||
Mandate.ParameterNotNullOrEmpty(alias, "alias");
|
||||
if (parent != null)
|
||||
{
|
||||
Mandate.That(parent.HasIdentity, () => new InvalidOperationException("The parent content type must have an identity"));
|
||||
}
|
||||
|
||||
var clone = original.DeepCloneWithResetIdentities(alias);
|
||||
|
||||
clone.Name = name;
|
||||
|
||||
var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList();
|
||||
//remove all composition that is not it's current alias
|
||||
foreach (var a in compositionAliases)
|
||||
{
|
||||
clone.RemoveContentType(a);
|
||||
}
|
||||
|
||||
//if a parent is specified set it's composition and parent
|
||||
if (parent != null)
|
||||
{
|
||||
//add a new parent composition
|
||||
clone.AddContentType(parent);
|
||||
clone.ParentId = parent.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
//set to root
|
||||
clone.ParentId = -1;
|
||||
}
|
||||
|
||||
Save(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IContentType"/> object by its Id
|
||||
/// </summary>
|
||||
|
||||
@@ -108,10 +108,11 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An enumerable list of string values</returns>
|
||||
public IEnumerable<string> GetPreValuesByDataTypeId(int id)
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var dtos = uow.Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = id });
|
||||
var list = dtos.Select(x => x.Value).ToList();
|
||||
var collection = repository.GetPreValuesCollectionByDataTypeId(id);
|
||||
//now convert the collection to a string list
|
||||
var list = collection.FormatAsDictionary().Select(x => x.Value.Value).ToList();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -123,12 +124,9 @@ namespace Umbraco.Core.Services
|
||||
/// <returns></returns>
|
||||
public PreValueCollection GetPreValuesCollectionByDataTypeId(int id)
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var dtos = uow.Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = id });
|
||||
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList();
|
||||
|
||||
return PreValueConverter.ConvertToPreValuesCollection(list);
|
||||
return repository.GetPreValuesCollectionByDataTypeId(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +137,9 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>PreValue as a string</returns>
|
||||
public string GetPreValueAsString(int id)
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var dto = uow.Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @Id", new { Id = id });
|
||||
return dto != null ? dto.Value : string.Empty;
|
||||
return repository.GetPreValueAsString(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,17 +153,14 @@ namespace Umbraco.Core.Services
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
|
||||
return;
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
uow.Commit();
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
uow.Commit();
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
|
||||
}
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
|
||||
@@ -182,31 +176,29 @@ namespace Umbraco.Core.Services
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions), this))
|
||||
return;
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
foreach (var dataTypeDefinition in dataTypeDefinitions)
|
||||
{
|
||||
foreach (var dataTypeDefinition in dataTypeDefinitions)
|
||||
{
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
}
|
||||
uow.Commit();
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions, false), this);
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
}
|
||||
uow.Commit();
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions, false), this);
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a list of PreValues for a given DataTypeDefinition
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="dataTypeId">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="values">List of string values to save</param>
|
||||
[Obsolete("This should no longer be used, use the alternative SavePreValues or SaveDataTypeAndPreValues methods instead. This will only insert pre-values without keys")]
|
||||
public void SavePreValues(int id, IEnumerable<string> values)
|
||||
public void SavePreValues(int dataTypeId, IEnumerable<string> values)
|
||||
{
|
||||
//TODO: Should we raise an event here since we are really saving values for the data type?
|
||||
|
||||
@@ -218,7 +210,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
var sortOrderObj =
|
||||
uow.Database.ExecuteScalar<object>(
|
||||
"SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id });
|
||||
"SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = dataTypeId });
|
||||
int sortOrder;
|
||||
if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false)
|
||||
{
|
||||
@@ -227,7 +219,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
var dto = new DataTypePreValueDto { DataTypeNodeId = id, Value = value, SortOrder = sortOrder };
|
||||
var dto = new DataTypePreValueDto { DataTypeNodeId = dataTypeId, Value = value, SortOrder = sortOrder };
|
||||
uow.Database.Insert(dto);
|
||||
sortOrder++;
|
||||
}
|
||||
@@ -241,26 +233,40 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Saves/updates the pre-values
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="dataTypeId"></param>
|
||||
/// <param name="values"></param>
|
||||
/// <remarks>
|
||||
/// We need to actually look up each pre-value and maintain it's id if possible - this is because of silly property editors
|
||||
/// like 'dropdown list publishing keys'
|
||||
/// </remarks>
|
||||
public void SavePreValues(int id, IDictionary<string, PreValue> values)
|
||||
public void SavePreValues(int dataTypeId, IDictionary<string, PreValue> values)
|
||||
{
|
||||
var dtd = this.GetDataTypeDefinitionById(dataTypeId);
|
||||
if (dtd == null)
|
||||
{
|
||||
throw new InvalidOperationException("No data type found for id " + dataTypeId);
|
||||
}
|
||||
SavePreValues(dtd, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves/updates the pre-values
|
||||
/// </summary>
|
||||
/// <param name="dataTypeDefinition"></param>
|
||||
/// <param name="values"></param>
|
||||
/// <remarks>
|
||||
/// We need to actually look up each pre-value and maintain it's id if possible - this is because of silly property editors
|
||||
/// like 'dropdown list publishing keys'
|
||||
/// </remarks>
|
||||
public void SavePreValues(IDataTypeDefinition dataTypeDefinition, IDictionary<string, PreValue> values)
|
||||
{
|
||||
//TODO: Should we raise an event here since we are really saving values for the data type?
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
using (var transaction = uow.Database.GetTransaction())
|
||||
{
|
||||
AddOrUpdatePreValues(id, values, uow);
|
||||
transaction.Complete();
|
||||
}
|
||||
}
|
||||
repository.AddOrUpdatePreValues(dataTypeDefinition, values);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,73 +281,25 @@ namespace Umbraco.Core.Services
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
|
||||
return;
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
var uow = (PetaPocoUnitOfWork)_uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
dataTypeDefinition.CreatorId = userId;
|
||||
|
||||
//complete the transaction, but run the delegate before the db transaction is finalized
|
||||
uow.Commit(database => AddOrUpdatePreValues(dataTypeDefinition.Id, values, uow));
|
||||
//add/update the dtd
|
||||
repository.AddOrUpdate(dataTypeDefinition);
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
|
||||
}
|
||||
//add/update the prevalues
|
||||
repository.AddOrUpdatePreValues(dataTypeDefinition, values);
|
||||
|
||||
uow.Commit();
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
|
||||
}
|
||||
|
||||
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
|
||||
}
|
||||
|
||||
private void AddOrUpdatePreValues(int id, IDictionary<string, PreValue> preValueCollection, IDatabaseUnitOfWork uow)
|
||||
{
|
||||
//first just get all pre-values for this data type so we can compare them to see if we need to insert or update or replace
|
||||
var sql = new Sql().Select("*")
|
||||
.From<DataTypePreValueDto>()
|
||||
.Where<DataTypePreValueDto>(dto => dto.DataTypeNodeId == id)
|
||||
.OrderBy<DataTypePreValueDto>(dto => dto.SortOrder);
|
||||
var currentVals = uow.Database.Fetch<DataTypePreValueDto>(sql).ToArray();
|
||||
|
||||
//already existing, need to be updated
|
||||
var valueIds = preValueCollection.Where(x => x.Value.Id > 0).Select(x => x.Value.Id).ToArray();
|
||||
var existingByIds = currentVals.Where(x => valueIds.Contains(x.Id)).ToArray();
|
||||
|
||||
//These ones need to be removed from the db, they no longer exist in the new values
|
||||
var deleteById = currentVals.Where(x => valueIds.Contains(x.Id) == false);
|
||||
|
||||
foreach (var d in deleteById)
|
||||
{
|
||||
uow.Database.Execute(
|
||||
"DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId AND id=@Id",
|
||||
new { DataTypeId = id, Id = d.Id });
|
||||
}
|
||||
|
||||
var sortOrder = 1;
|
||||
|
||||
foreach (var pre in preValueCollection)
|
||||
{
|
||||
var existing = existingByIds.FirstOrDefault(valueDto => valueDto.Id == pre.Value.Id);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Value = pre.Value.Value;
|
||||
existing.SortOrder = sortOrder;
|
||||
uow.Database.Update(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dto = new DataTypePreValueDto
|
||||
{
|
||||
DataTypeNodeId = id,
|
||||
Value = pre.Value.Value,
|
||||
SortOrder = sortOrder,
|
||||
Alias = pre.Key
|
||||
};
|
||||
uow.Database.Insert(dto);
|
||||
}
|
||||
|
||||
sortOrder++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an <see cref="IDataTypeDefinition"/>
|
||||
@@ -358,31 +316,9 @@ namespace Umbraco.Core.Services
|
||||
return;
|
||||
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentTypeRepository(uow))
|
||||
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
|
||||
{
|
||||
//Find ContentTypes using this IDataTypeDefinition on a PropertyType
|
||||
var query = Query<PropertyType>.Builder.Where(x => x.DataTypeDefinitionId == dataTypeDefinition.Id);
|
||||
var contentTypes = repository.GetByQuery(query);
|
||||
|
||||
//Loop through the list of results and remove the PropertyTypes that references the DataTypeDefinition that is being deleted
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
if (contentType == null) continue;
|
||||
|
||||
foreach (var group in contentType.PropertyGroups)
|
||||
{
|
||||
var types = @group.PropertyTypes.Where(x => x.DataTypeDefinitionId == dataTypeDefinition.Id).ToList();
|
||||
foreach (var propertyType in types)
|
||||
{
|
||||
@group.PropertyTypes.Remove(propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
repository.AddOrUpdate(contentType);
|
||||
}
|
||||
|
||||
var dataTypeRepository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow);
|
||||
dataTypeRepository.Delete(dataTypeDefinition);
|
||||
repository.Delete(dataTypeDefinition);
|
||||
|
||||
uow.Commit();
|
||||
|
||||
@@ -435,40 +371,6 @@ namespace Umbraco.Core.Services
|
||||
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<IDataTypeDefinition>> Saved;
|
||||
#endregion
|
||||
|
||||
internal static class PreValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the tuple to a pre-value collection
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
internal static PreValueCollection ConvertToPreValuesCollection(IEnumerable<Tuple<PreValue, string, int>> list)
|
||||
{
|
||||
//now we need to determine if they are dictionary based, otherwise they have to be array based
|
||||
var dictionary = new Dictionary<string, PreValue>();
|
||||
|
||||
//need to check all of the keys, if there's only one and it is empty then it's an array
|
||||
var keys = list.Select(x => x.Item2).Distinct().ToArray();
|
||||
if (keys.Length == 1 && keys[0].IsNullOrWhiteSpace())
|
||||
{
|
||||
return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1));
|
||||
}
|
||||
|
||||
foreach (var item in list
|
||||
.OrderBy(x => x.Item3) //we'll order them first so we maintain the order index in the dictionary
|
||||
.GroupBy(x => x.Item2)) //group by alias
|
||||
{
|
||||
if (item.Count() > 1)
|
||||
{
|
||||
//if there's more than 1 item per key, then it cannot be a dictionary, just return the array
|
||||
return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1));
|
||||
}
|
||||
|
||||
dictionary.Add(item.Key, item.First().Item1);
|
||||
}
|
||||
|
||||
return new PreValueCollection(dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Strings;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
//TODO: Move the rest of the logic for the PackageService.Export methods to here!
|
||||
|
||||
/// <summary>
|
||||
/// A helper class to serialize entities to XML
|
||||
/// </summary>
|
||||
internal class EntityXmlSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Exports an <see cref="IContent"/> item to xml as an <see cref="XElement"/>
|
||||
/// </summary>
|
||||
/// <param name="contentService"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="content">Content to export</param>
|
||||
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Content object</returns>
|
||||
public XElement Serialize(IContentService contentService, IDataTypeService dataTypeService, IContent content, bool deep = false)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Serialize(dataTypeService, content, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", content.ContentType.Id));
|
||||
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile().Name));
|
||||
xml.Add(new XAttribute("writerName", content.GetWriterProfile().Name));
|
||||
xml.Add(new XAttribute("writerID", content.WriterId));
|
||||
xml.Add(new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString(CultureInfo.InvariantCulture)));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias));
|
||||
|
||||
if (deep)
|
||||
{
|
||||
var descendants = contentService.GetDescendants(content).ToArray();
|
||||
var currentChildren = descendants.Where(x => x.ParentId == content.Id);
|
||||
AddChildXml(contentService, dataTypeService, descendants, currentChildren, xml);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
|
||||
/// </summary>
|
||||
/// <param name="mediaService"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="media">Media to export</param>
|
||||
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
|
||||
public XElement Serialize(IMediaService mediaService, IDataTypeService dataTypeService, IMedia media, bool deep = false)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : media.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Serialize(dataTypeService, media, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", media.ContentType.Id));
|
||||
xml.Add(new XAttribute("writerName", media.GetCreatorProfile().Name));
|
||||
xml.Add(new XAttribute("writerID", media.CreatorId));
|
||||
xml.Add(new XAttribute("version", media.Version));
|
||||
xml.Add(new XAttribute("template", 0));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias));
|
||||
|
||||
if (deep)
|
||||
{
|
||||
var descendants = mediaService.GetDescendants(media).ToArray();
|
||||
var currentChildren = descendants.Where(x => x.ParentId == media.Id);
|
||||
AddChildXml(mediaService, dataTypeService, descendants, currentChildren, xml);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
|
||||
/// </summary>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="member">Member to export</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Member object</returns>
|
||||
public XElement Serialize(IDataTypeService dataTypeService, IMember member)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : member.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Serialize(dataTypeService, member, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", member.ContentType.Id));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", member.ContentType.Alias));
|
||||
|
||||
xml.Add(new XAttribute("loginName", member.Username));
|
||||
xml.Add(new XAttribute("email", member.Email));
|
||||
xml.Add(new XAttribute("key", member.Key));
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
public XElement Serialize(IDataTypeService dataTypeService, Property property)
|
||||
{
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
|
||||
var xd = new XmlDocument();
|
||||
var xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
|
||||
//Add the property alias to the legacy schema
|
||||
if (UmbracoSettings.UseLegacyXmlSchema)
|
||||
{
|
||||
var alias = xd.CreateAttribute("alias");
|
||||
alias.Value = property.Alias.ToSafeAlias();
|
||||
xmlNode.Attributes.Append(alias);
|
||||
}
|
||||
|
||||
//This seems to fail during testing
|
||||
//SD: With the new null checks below, this shouldn't fail anymore.
|
||||
var dt = property.PropertyType.DataType(property.Id, dataTypeService);
|
||||
if (dt != null && dt.Data != null)
|
||||
{
|
||||
//We've already got the value for the property so we're going to give it to the
|
||||
// data type's data property so it doesn't go re-look up the value from the db again.
|
||||
var defaultData = dt.Data as IDataValueSetter;
|
||||
if (defaultData != null)
|
||||
{
|
||||
defaultData.SetValue(property.Value, property.PropertyType.DataTypeDatabaseType.ToString());
|
||||
}
|
||||
|
||||
xmlNode.AppendChild(dt.Data.ToXMl(xd));
|
||||
}
|
||||
|
||||
var element = xmlNode.GetXElement();
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Media Export to recursively add children
|
||||
/// </summary>
|
||||
/// <param name="mediaService"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="originalDescendants"></param>
|
||||
/// <param name="currentChildren"></param>
|
||||
/// <param name="currentXml"></param>
|
||||
private void AddChildXml(IMediaService mediaService, IDataTypeService dataTypeService, IMedia[] originalDescendants, IEnumerable<IMedia> currentChildren, XElement currentXml)
|
||||
{
|
||||
foreach (var child in currentChildren)
|
||||
{
|
||||
//add the child's xml
|
||||
var childXml = Serialize(mediaService, dataTypeService, child);
|
||||
currentXml.Add(childXml);
|
||||
//copy local (out of closure)
|
||||
var c = child;
|
||||
//get this item's children
|
||||
var children = originalDescendants.Where(x => x.ParentId == c.Id);
|
||||
//recurse and add it's children to the child xml element
|
||||
AddChildXml(mediaService, dataTypeService, originalDescendants, children, childXml);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Part of the export of IContent and IMedia and IMember which is shared
|
||||
/// </summary>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="contentBase">Base Content or Media to export</param>
|
||||
/// <param name="nodeName">Name of the node</param>
|
||||
/// <returns><see cref="XElement"/></returns>
|
||||
private XElement Serialize(IDataTypeService dataTypeService, IContentBase contentBase, string nodeName)
|
||||
{
|
||||
//NOTE: that one will take care of umbracoUrlName
|
||||
var url = contentBase.GetUrlSegment();
|
||||
|
||||
var xml = new XElement(nodeName,
|
||||
new XAttribute("id", contentBase.Id),
|
||||
new XAttribute("parentID", contentBase.Level > 1 ? contentBase.ParentId : -1),
|
||||
new XAttribute("level", contentBase.Level),
|
||||
new XAttribute("creatorID", contentBase.CreatorId),
|
||||
new XAttribute("sortOrder", contentBase.SortOrder),
|
||||
new XAttribute("createDate", contentBase.CreateDate.ToString("s")),
|
||||
new XAttribute("updateDate", contentBase.UpdateDate.ToString("s")),
|
||||
new XAttribute("nodeName", contentBase.Name),
|
||||
new XAttribute("urlName", url),
|
||||
new XAttribute("path", contentBase.Path),
|
||||
new XAttribute("isDoc", ""));
|
||||
|
||||
foreach (var property in contentBase.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
xml.Add(Serialize(dataTypeService, property));
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Content Export to recursively add children
|
||||
/// </summary>
|
||||
/// <param name="contentService"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="originalDescendants"></param>
|
||||
/// <param name="currentChildren"></param>
|
||||
/// <param name="currentXml"></param>
|
||||
private void AddChildXml(IContentService contentService, IDataTypeService dataTypeService, IContent[] originalDescendants, IEnumerable<IContent> currentChildren, XElement currentXml)
|
||||
{
|
||||
foreach (var child in currentChildren)
|
||||
{
|
||||
//add the child's xml
|
||||
var childXml = Serialize(contentService, dataTypeService, child);
|
||||
currentXml.Add(childXml);
|
||||
//copy local (out of closure)
|
||||
var c = child;
|
||||
//get this item's children
|
||||
var children = originalDescendants.Where(x => x.ParentId == c.Id);
|
||||
//recurse and add it's children to the child xml element
|
||||
AddChildXml(contentService, dataTypeService, originalDescendants, children, childXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,42 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IContentTypeService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
|
||||
/// </summary>
|
||||
/// <param name="original">
|
||||
/// The content type to copy
|
||||
/// </param>
|
||||
/// <param name="alias">
|
||||
/// The new alias of the content type
|
||||
/// </param>
|
||||
/// <param name="name">
|
||||
/// The new name of the content type
|
||||
/// </param>
|
||||
/// <param name="parentId">
|
||||
/// The parent to copy the content type to, default is -1 (root)
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IContentType Copy(IContentType original, string alias, string name, int parentId = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
|
||||
/// </summary>
|
||||
/// <param name="original">
|
||||
/// The content type to copy
|
||||
/// </param>
|
||||
/// <param name="alias">
|
||||
/// The new alias of the content type
|
||||
/// </param>
|
||||
/// <param name="name">
|
||||
/// The new name of the content type
|
||||
/// </param>
|
||||
/// <param name="parent">
|
||||
/// The parent to copy the content type to
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IContentType Copy(IContentType original, string alias, string name, IContentType parent);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IContentType"/> object by its Id
|
||||
/// </summary>
|
||||
|
||||
@@ -93,17 +93,24 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Saves a list of PreValues for a given DataTypeDefinition
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="dataTypeId">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="values">List of string values to save</param>
|
||||
[Obsolete("This should no longer be used, use the alternative SavePreValues or SaveDataTypeAndPreValues methods instead. This will only insert pre-values without keys")]
|
||||
void SavePreValues(int id, IEnumerable<string> values);
|
||||
void SavePreValues(int dataTypeId, IEnumerable<string> values);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a list of PreValues for a given DataTypeDefinition
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="dataTypeId">Id of the DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="values">List of key/value pairs to save</param>
|
||||
void SavePreValues(int id, IDictionary<string, PreValue> values);
|
||||
void SavePreValues(int dataTypeId, IDictionary<string, PreValue> values);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a list of PreValues for a given DataTypeDefinition
|
||||
/// </summary>
|
||||
/// <param name="dataTypeDefinition">The DataTypeDefinition to save PreValues for</param>
|
||||
/// <param name="values">List of key/value pairs to save</param>
|
||||
void SavePreValues(IDataTypeDefinition dataTypeDefinition, IDictionary<string, PreValue> values);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the data type and it's prevalues
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
@@ -26,6 +27,8 @@ namespace Umbraco.Core.Services
|
||||
//Support recursive locks because some of the methods that require locking call other methods that require locking.
|
||||
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public MediaService(RepositoryFactory repositoryFactory)
|
||||
: this(new PetaPocoUnitOfWorkProvider(), repositoryFactory)
|
||||
@@ -36,6 +39,14 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
_uowProvider = provider;
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_dataTypeService = new DataTypeService(provider, repositoryFactory);
|
||||
}
|
||||
|
||||
public MediaService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IDataTypeService dataTypeService)
|
||||
{
|
||||
_uowProvider = provider;
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -146,10 +157,15 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
media.CreatorId = userId;
|
||||
repository.AddOrUpdate(media);
|
||||
uow.Commit();
|
||||
|
||||
var xml = media.ToXml();
|
||||
CreateAndSaveMediaXml(xml, media.Id, uow.Database);
|
||||
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,10 +217,14 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
media.CreatorId = userId;
|
||||
repository.AddOrUpdate(media);
|
||||
uow.Commit();
|
||||
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
}
|
||||
|
||||
var xml = media.ToXml();
|
||||
CreateAndSaveMediaXml(xml, media.Id, uow.Database);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,15 +536,29 @@ namespace Umbraco.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
if (Moving.IsRaisedEventCancelled(new MoveEventArgs<IMedia>(media, parentId), this))
|
||||
return;
|
||||
var originalPath = media.Path;
|
||||
|
||||
if (Moving.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IMedia>(
|
||||
new MoveEventInfo<IMedia>(media, originalPath, parentId)), this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
media.ParentId = parentId;
|
||||
if (media.Trashed)
|
||||
{
|
||||
media.ChangeTrashedState(false, parentId);
|
||||
}
|
||||
Save(media, userId);
|
||||
Save(media, userId,
|
||||
//no events!
|
||||
false);
|
||||
|
||||
//used to track all the moved entities to be given to the event
|
||||
var moveInfo = new List<MoveEventInfo<IMedia>>
|
||||
{
|
||||
new MoveEventInfo<IMedia>(media, originalPath, parentId)
|
||||
};
|
||||
|
||||
//Ensure that relevant properties are updated on children
|
||||
var children = GetChildren(media.Id).ToArray();
|
||||
@@ -533,11 +567,13 @@ namespace Umbraco.Core.Services
|
||||
var parentPath = media.Path;
|
||||
var parentLevel = media.Level;
|
||||
var parentTrashed = media.Trashed;
|
||||
var updatedDescendants = UpdatePropertiesOnChildren(children, parentPath, parentLevel, parentTrashed);
|
||||
Save(updatedDescendants, userId);
|
||||
var updatedDescendants = UpdatePropertiesOnChildren(children, parentPath, parentLevel, parentTrashed, moveInfo);
|
||||
Save(updatedDescendants, userId,
|
||||
//no events!
|
||||
false);
|
||||
}
|
||||
|
||||
Moved.RaiseEvent(new MoveEventArgs<IMedia>(media, false, parentId), this);
|
||||
Moved.RaiseEvent(new MoveEventArgs<IMedia>(false, moveInfo.ToArray()), this);
|
||||
|
||||
Audit.Add(AuditTypes.Move, "Move Media performed by user", userId, media.Id);
|
||||
}
|
||||
@@ -552,8 +588,19 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
if (media == null) throw new ArgumentNullException("media");
|
||||
|
||||
if (Trashing.IsRaisedEventCancelled(new MoveEventArgs<IMedia>(media, -21), this))
|
||||
var originalPath = media.Path;
|
||||
|
||||
if (Trashing.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IMedia>(
|
||||
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var moveInfo = new List<MoveEventInfo<IMedia>>
|
||||
{
|
||||
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
|
||||
};
|
||||
|
||||
//Find Descendants, which will be moved to the recycle bin along with the parent/grandparent.
|
||||
var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList();
|
||||
@@ -561,12 +608,14 @@ namespace Umbraco.Core.Services
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
//TODO: This should be part of the repo!
|
||||
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished media
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = media.Id });
|
||||
|
||||
media.ChangeTrashedState(true, -21);
|
||||
media.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
|
||||
repository.AddOrUpdate(media);
|
||||
|
||||
|
||||
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
|
||||
foreach (var descendant in descendants)
|
||||
{
|
||||
@@ -575,12 +624,14 @@ namespace Umbraco.Core.Services
|
||||
|
||||
descendant.ChangeTrashedState(true, descendant.ParentId);
|
||||
repository.AddOrUpdate(descendant);
|
||||
|
||||
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Trashed.RaiseEvent(new MoveEventArgs<IMedia>(media, false, -21), this);
|
||||
Trashed.RaiseEvent(new MoveEventArgs<IMedia>(false, moveInfo.ToArray()), this);
|
||||
|
||||
Audit.Add(AuditTypes.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
|
||||
}
|
||||
@@ -768,10 +819,14 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
media.CreatorId = userId;
|
||||
repository.AddOrUpdate(media);
|
||||
uow.Commit();
|
||||
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
}
|
||||
|
||||
var xml = media.ToXml();
|
||||
CreateAndSaveMediaXml(xml, media.Id, uow.Database);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,33 +844,37 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
|
||||
public void Save(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = medias.ToArray();
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(medias), this))
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(asArray), this))
|
||||
return;
|
||||
}
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
foreach (var media in medias)
|
||||
foreach (var media in asArray)
|
||||
{
|
||||
media.CreatorId = userId;
|
||||
repository.AddOrUpdate(media);
|
||||
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
}
|
||||
}
|
||||
|
||||
//commit the whole lot in one go
|
||||
uow.Commit();
|
||||
|
||||
foreach (var media in medias)
|
||||
{
|
||||
CreateAndSaveMediaXml(media.ToXml(), media.Id, uow.Database);
|
||||
}
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMedia>(medias, false), this);
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMedia>(asArray, false), this);
|
||||
|
||||
Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId, -1);
|
||||
}
|
||||
@@ -831,21 +890,21 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(IEnumerable<IMedia> items, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = items.ToArray();
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(items), this))
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(asArray), this))
|
||||
return false;
|
||||
}
|
||||
|
||||
var shouldBeCached = new List<IMedia>();
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var media in items)
|
||||
foreach (var media in asArray)
|
||||
{
|
||||
//If the current sort order equals that of the media
|
||||
//we don't need to update it, so just increment the sort order
|
||||
@@ -860,28 +919,28 @@ namespace Umbraco.Core.Services
|
||||
i++;
|
||||
|
||||
repository.AddOrUpdate(media);
|
||||
shouldBeCached.Add(media);
|
||||
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, m));
|
||||
}
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
|
||||
foreach (var content in shouldBeCached)
|
||||
{
|
||||
//Create and Save ContentXml DTO
|
||||
var xml = content.ToXml();
|
||||
CreateAndSaveMediaXml(xml, content.Id, uow.Database);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMedia>(items, false), this);
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMedia>(asArray, false), this);
|
||||
|
||||
Audit.Add(AuditTypes.Sort, "Sorting Media performed by user", userId, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO: This needs to be put into the MediaRepository, all CUD logic!
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds all xml content in the cmsContentXml table for all media
|
||||
/// </summary>
|
||||
@@ -919,7 +978,7 @@ namespace Umbraco.Core.Services
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
var xml = c.ToXml();
|
||||
var xml = _entitySerializer.Serialize(this, _dataTypeService, c);
|
||||
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
|
||||
}
|
||||
|
||||
@@ -978,12 +1037,14 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="parentPath">Path of the Parent media</param>
|
||||
/// <param name="parentLevel">Level of the Parent media</param>
|
||||
/// <param name="parentTrashed">Indicates whether the Parent is trashed or not</param>
|
||||
/// <param name="eventInfo">Used to track the objects to be used in the move event</param>
|
||||
/// <returns>Collection of updated <see cref="IMedia"/> objects</returns>
|
||||
private IEnumerable<IMedia> UpdatePropertiesOnChildren(IEnumerable<IMedia> children, string parentPath, int parentLevel, bool parentTrashed)
|
||||
private IEnumerable<IMedia> UpdatePropertiesOnChildren(IEnumerable<IMedia> children, string parentPath, int parentLevel, bool parentTrashed, ICollection<MoveEventInfo<IMedia>> eventInfo)
|
||||
{
|
||||
var list = new List<IMedia>();
|
||||
foreach (var child in children)
|
||||
{
|
||||
var originalPath = child.Path;
|
||||
child.Path = string.Concat(parentPath, ",", child.Id);
|
||||
child.Level = parentLevel + 1;
|
||||
if (parentTrashed != child.Trashed)
|
||||
@@ -991,23 +1052,24 @@ namespace Umbraco.Core.Services
|
||||
child.ChangeTrashedState(parentTrashed, child.ParentId);
|
||||
}
|
||||
|
||||
eventInfo.Add(new MoveEventInfo<IMedia>(child, originalPath, child.ParentId));
|
||||
list.Add(child);
|
||||
|
||||
var grandkids = GetChildren(child.Id).ToArray();
|
||||
if (grandkids.Any())
|
||||
{
|
||||
list.AddRange(UpdatePropertiesOnChildren(grandkids, child.Path, child.Level, child.Trashed));
|
||||
list.AddRange(UpdatePropertiesOnChildren(grandkids, child.Path, child.Level, child.Trashed, eventInfo));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void CreateAndSaveMediaXml(XElement xml, int id, UmbracoDatabase db)
|
||||
{
|
||||
var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var exists = db.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = id }) != null;
|
||||
int result = exists ? db.Update(poco) : Convert.ToInt32(db.Insert(poco));
|
||||
}
|
||||
//private void CreateAndSaveMediaXml(XElement xml, int id, UmbracoDatabase db)
|
||||
//{
|
||||
// var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) };
|
||||
// var exists = db.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = id }) != null;
|
||||
// int result = exists ? db.Update(poco) : Convert.ToInt32(db.Insert(poco));
|
||||
//}
|
||||
|
||||
private IMediaType FindMediaTypeByAlias(string mediaTypeAlias)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading;
|
||||
using System.Web.Security;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Auditing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -20,6 +21,7 @@ using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents the MemberService.
|
||||
/// </summary>
|
||||
@@ -28,7 +30,8 @@ namespace Umbraco.Core.Services
|
||||
private readonly RepositoryFactory _repositoryFactory;
|
||||
private readonly IMemberGroupService _memberGroupService;
|
||||
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
|
||||
|
||||
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
|
||||
|
||||
public MemberService(RepositoryFactory repositoryFactory, IMemberGroupService memberGroupService)
|
||||
@@ -49,6 +52,24 @@ namespace Umbraco.Core.Services
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_memberGroupService = memberGroupService;
|
||||
_uowProvider = provider;
|
||||
_dataTypeService = new DataTypeService(provider, repositoryFactory);
|
||||
}
|
||||
|
||||
public MemberService(IDatabaseUnitOfWorkProvider provider, IMemberGroupService memberGroupService, IDataTypeService dataTypeService)
|
||||
: this(provider, new RepositoryFactory(), memberGroupService, dataTypeService)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MemberService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IMemberGroupService memberGroupService, IDataTypeService dataTypeService)
|
||||
{
|
||||
if (provider == null) throw new ArgumentNullException("provider");
|
||||
if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory");
|
||||
if (memberGroupService == null) throw new ArgumentNullException("memberGroupService");
|
||||
_repositoryFactory = repositoryFactory;
|
||||
_memberGroupService = memberGroupService;
|
||||
_uowProvider = provider;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
#region IMemberService Implementation
|
||||
@@ -702,7 +723,7 @@ namespace Umbraco.Core.Services
|
||||
IMember IMembershipMemberService<IMember>.CreateWithIdentity(string username, string email, string rawPasswordValue, string memberTypeAlias)
|
||||
{
|
||||
var memberType = FindMemberTypeByAlias(memberTypeAlias);
|
||||
return CreateMemberWithIdentity(username, email, memberType);
|
||||
return CreateMemberWithIdentity(username, email, username, rawPasswordValue, memberType);
|
||||
}
|
||||
|
||||
private IMember CreateMemberWithIdentity(string username, string email, string name, string rawPasswordValue, IMemberType memberType)
|
||||
@@ -721,11 +742,15 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
|
||||
{
|
||||
repository.AddOrUpdate(member);
|
||||
uow.Commit();
|
||||
|
||||
//insert the xml
|
||||
var xml = member.ToXml();
|
||||
CreateAndSaveMemberXml(xml, member.Id, uow.Database);
|
||||
repository.AddOrUpdateContentXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMember>(member, false), this);
|
||||
@@ -829,10 +854,14 @@ namespace Umbraco.Core.Services
|
||||
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
|
||||
{
|
||||
repository.AddOrUpdate(entity);
|
||||
uow.Commit();
|
||||
repository.AddOrUpdateContentXml(entity, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(entity, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
}
|
||||
|
||||
var xml = entity.ToXml();
|
||||
CreateAndSaveMemberXml(xml, entity.Id, uow.Database);
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
@@ -841,9 +870,11 @@ namespace Umbraco.Core.Services
|
||||
|
||||
public void Save(IEnumerable<IMember> entities, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = entities.ToArray();
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMember>(entities), this))
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMember>(asArray), this))
|
||||
return;
|
||||
}
|
||||
using (new WriteLock(Locker))
|
||||
@@ -851,22 +882,23 @@ namespace Umbraco.Core.Services
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
|
||||
{
|
||||
foreach (var member in entities)
|
||||
foreach (var member in asArray)
|
||||
{
|
||||
repository.AddOrUpdate(member);
|
||||
repository.AddOrUpdateContentXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
// generate preview for blame history?
|
||||
if (UmbracoSettings.EnableGlobalPreviewStorage)
|
||||
{
|
||||
repository.AddOrUpdatePreviewXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
|
||||
}
|
||||
}
|
||||
|
||||
//commit the whole lot in one go
|
||||
uow.Commit();
|
||||
|
||||
foreach (var member in entities)
|
||||
{
|
||||
CreateAndSaveMemberXml(member.ToXml(), member.Id, uow.Database);
|
||||
}
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMember>(entities, false), this);
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMember>(asArray, false), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1104,7 @@ namespace Umbraco.Core.Services
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
var xml = c.ToXml();
|
||||
var xml = _entitySerializer.Serialize(_dataTypeService, c);
|
||||
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
|
||||
}
|
||||
|
||||
@@ -1121,14 +1153,7 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateAndSaveMemberXml(XElement xml, int id, UmbracoDatabase db)
|
||||
{
|
||||
var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) };
|
||||
var exists = db.FirstOrDefault<ContentXmlDto>("WHERE nodeId = @Id", new { Id = id }) != null;
|
||||
int result = exists ? db.Update(poco) : Convert.ToInt32(db.Insert(poco));
|
||||
}
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,9 +5,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -17,7 +15,6 @@ using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
@@ -69,79 +66,8 @@ namespace Umbraco.Core.Services
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Content object</returns>
|
||||
public XElement Export(IContent content, bool deep = false)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Export(content, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", content.ContentType.Id));
|
||||
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile().Name));
|
||||
xml.Add(new XAttribute("writerName", content.GetWriterProfile().Name));
|
||||
xml.Add(new XAttribute("writerID", content.WriterId));
|
||||
xml.Add(new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString(CultureInfo.InvariantCulture)));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias));
|
||||
|
||||
if (deep)
|
||||
{
|
||||
var descendants = content.Descendants().ToArray();
|
||||
var currentChildren = descendants.Where(x => x.ParentId == content.Id);
|
||||
AddChildXml(descendants, currentChildren, xml);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Part of the export of IContent and IMedia and IMember which is shared
|
||||
/// </summary>
|
||||
/// <param name="contentBase">Base Content or Media to export</param>
|
||||
/// <param name="nodeName">Name of the node</param>
|
||||
/// <returns><see cref="XElement"/></returns>
|
||||
private XElement Export(IContentBase contentBase, string nodeName)
|
||||
{
|
||||
//NOTE: that one will take care of umbracoUrlName
|
||||
var url = contentBase.GetUrlSegment();
|
||||
|
||||
var xml = new XElement(nodeName,
|
||||
new XAttribute("id", contentBase.Id),
|
||||
new XAttribute("parentID", contentBase.Level > 1 ? contentBase.ParentId : -1),
|
||||
new XAttribute("level", contentBase.Level),
|
||||
new XAttribute("creatorID", contentBase.CreatorId),
|
||||
new XAttribute("sortOrder", contentBase.SortOrder),
|
||||
new XAttribute("createDate", contentBase.CreateDate.ToString("s")),
|
||||
new XAttribute("updateDate", contentBase.UpdateDate.ToString("s")),
|
||||
new XAttribute("nodeName", contentBase.Name),
|
||||
new XAttribute("urlName", url),
|
||||
new XAttribute("path", contentBase.Path),
|
||||
new XAttribute("isDoc", ""));
|
||||
|
||||
foreach (var property in contentBase.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
xml.Add(property.ToXml());
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Content Export to recursively add children
|
||||
/// </summary>
|
||||
/// <param name="originalDescendants"></param>
|
||||
/// <param name="currentChildren"></param>
|
||||
/// <param name="currentXml"></param>
|
||||
private void AddChildXml(IContent[] originalDescendants, IEnumerable<IContent> currentChildren, XElement currentXml)
|
||||
{
|
||||
foreach (var child in currentChildren)
|
||||
{
|
||||
//add the child's xml
|
||||
var childXml = Export(child);
|
||||
currentXml.Add(childXml);
|
||||
//copy local (out of closure)
|
||||
var c = child;
|
||||
//get this item's children
|
||||
var children = originalDescendants.Where(x => x.ParentId == c.Id);
|
||||
//recurse and add it's children to the child xml element
|
||||
AddChildXml(originalDescendants, children, childXml);
|
||||
}
|
||||
var exporter = new EntityXmlSerializer();
|
||||
return exporter.Serialize(_contentService, _dataTypeService, content, deep);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -786,11 +712,13 @@ namespace Umbraco.Core.Services
|
||||
.Select(x => x.Attribute("Value").Value);
|
||||
|
||||
var valuesWithKeys = prevaluesElement.Elements("PreValue")
|
||||
.Where(x => ((string) x.Attribute("Alias")).IsNullOrWhiteSpace() == false)
|
||||
.ToDictionary(key => (string) key.Attribute("Alias"), val => new PreValue((string) val.Attribute("Value")));
|
||||
.Where(x => ((string) x.Attribute("Alias")).IsNullOrWhiteSpace() == false)
|
||||
.ToDictionary(
|
||||
key => (string) key.Attribute("Alias"),
|
||||
val => new PreValue((string) val.Attribute("Value")));
|
||||
|
||||
//save the values with keys
|
||||
_dataTypeService.SavePreValues(dataTypeDefinition.Id, valuesWithKeys);
|
||||
_dataTypeService.SavePreValues(dataTypeDefinition, valuesWithKeys);
|
||||
|
||||
//save the values without keys (this is legacy)
|
||||
_dataTypeService.SavePreValues(dataTypeDefinition.Id, valuesWithoutKeys);
|
||||
@@ -840,15 +768,15 @@ namespace Umbraco.Core.Services
|
||||
return ImportDictionaryItems(dictionaryItemElementList, languages);
|
||||
}
|
||||
|
||||
private IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, List<ILanguage> languages)
|
||||
private IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, List<ILanguage> languages, Guid? parentId = null)
|
||||
{
|
||||
var items = new List<IDictionaryItem>();
|
||||
foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem"))
|
||||
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages));
|
||||
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, parentId));
|
||||
return items;
|
||||
}
|
||||
|
||||
private IEnumerable<IDictionaryItem> ImportDictionaryItem(XElement dictionaryItemElement, List<ILanguage> languages)
|
||||
private IEnumerable<IDictionaryItem> ImportDictionaryItem(XElement dictionaryItemElement, List<ILanguage> languages, Guid? parentId)
|
||||
{
|
||||
var items = new List<IDictionaryItem>();
|
||||
|
||||
@@ -857,10 +785,10 @@ namespace Umbraco.Core.Services
|
||||
if (_localizationService.DictionaryItemExists(key))
|
||||
dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages);
|
||||
else
|
||||
dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages);
|
||||
dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId);
|
||||
_localizationService.Save(dictionaryItem);
|
||||
items.Add(dictionaryItem);
|
||||
items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages));
|
||||
items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, dictionaryItem.Key));
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -874,9 +802,9 @@ namespace Umbraco.Core.Services
|
||||
return dictionaryItem;
|
||||
}
|
||||
|
||||
private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List<ILanguage> languages)
|
||||
private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List<ILanguage> languages, Guid? parentId)
|
||||
{
|
||||
var dictionaryItem = new DictionaryItem(key);
|
||||
var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key);
|
||||
var translations = new List<IDictionaryTranslation>();
|
||||
|
||||
foreach (var valueElement in dictionaryItemElement.Elements("Value"))
|
||||
@@ -962,20 +890,10 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
/// <param name="member">Member to export</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Member object</returns>
|
||||
internal XElement Export(IMember member)
|
||||
public XElement Export(IMember member)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : member.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Export(member, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", member.ContentType.Id));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", member.ContentType.Alias));
|
||||
|
||||
xml.Add(new XAttribute("loginName", member.Username));
|
||||
xml.Add(new XAttribute("email", member.Email));
|
||||
xml.Add(new XAttribute("key", member.Key));
|
||||
|
||||
return xml;
|
||||
var exporter = new EntityXmlSerializer();
|
||||
return exporter.Serialize(_dataTypeService, member);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -988,49 +906,10 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="media">Media to export</param>
|
||||
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
|
||||
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
|
||||
internal XElement Export(IMedia media, bool deep = false)
|
||||
public XElement Export(IMedia media, bool deep = false)
|
||||
{
|
||||
//nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : media.ContentType.Alias.ToSafeAliasWithForcingCheck();
|
||||
|
||||
var xml = Export(media, nodeName);
|
||||
xml.Add(new XAttribute("nodeType", media.ContentType.Id));
|
||||
xml.Add(new XAttribute("writerName", media.GetCreatorProfile().Name));
|
||||
xml.Add(new XAttribute("writerID", media.CreatorId));
|
||||
xml.Add(new XAttribute("version", media.Version));
|
||||
xml.Add(new XAttribute("template", 0));
|
||||
xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias));
|
||||
|
||||
if (deep)
|
||||
{
|
||||
var descendants = media.Descendants().ToArray();
|
||||
var currentChildren = descendants.Where(x => x.ParentId == media.Id);
|
||||
AddChildXml(descendants, currentChildren, xml);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by Media Export to recursively add children
|
||||
/// </summary>
|
||||
/// <param name="originalDescendants"></param>
|
||||
/// <param name="currentChildren"></param>
|
||||
/// <param name="currentXml"></param>
|
||||
private void AddChildXml(IMedia[] originalDescendants, IEnumerable<IMedia> currentChildren, XElement currentXml)
|
||||
{
|
||||
foreach (var child in currentChildren)
|
||||
{
|
||||
//add the child's xml
|
||||
var childXml = Export(child);
|
||||
currentXml.Add(childXml);
|
||||
//copy local (out of closure)
|
||||
var c = child;
|
||||
//get this item's children
|
||||
var children = originalDescendants.Where(x => x.ParentId == c.Id);
|
||||
//recurse and add it's children to the child xml element
|
||||
AddChildXml(originalDescendants, children, childXml);
|
||||
}
|
||||
var exporter = new EntityXmlSerializer();
|
||||
return exporter.Serialize(_mediaService, _dataTypeService, media, deep);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -43,10 +43,10 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="packagingService"></param>
|
||||
/// <param name="entityService"></param>
|
||||
/// <param name="relationService"></param>
|
||||
/// <param name="sectionService"></param>
|
||||
/// <param name="treeService"></param>
|
||||
/// <param name="tagService"></param>
|
||||
/// <param name="memberGroupService"></param>
|
||||
/// <param name="memberTypeService"></param>
|
||||
/// <param name="memberService"></param>
|
||||
/// <param name="userService"></param>
|
||||
public ServiceContext(
|
||||
IContentService contentService,
|
||||
IMediaService mediaService,
|
||||
@@ -57,7 +57,11 @@ namespace Umbraco.Core.Services
|
||||
PackagingService packagingService,
|
||||
IEntityService entityService,
|
||||
IRelationService relationService,
|
||||
IMemberGroupService memberGroupService/*,
|
||||
IMemberGroupService memberGroupService,
|
||||
IMemberTypeService memberTypeService,
|
||||
IMemberService memberService,
|
||||
IUserService userService
|
||||
/*,
|
||||
ISectionService sectionService,
|
||||
IApplicationTreeService treeService*/)
|
||||
{
|
||||
@@ -71,6 +75,10 @@ namespace Umbraco.Core.Services
|
||||
_entityService = new Lazy<IEntityService>(() => entityService);
|
||||
_relationService = new Lazy<IRelationService>(() => relationService);
|
||||
_memberGroupService = new Lazy<IMemberGroupService>(() => memberGroupService);
|
||||
_memberTypeService = new Lazy<IMemberTypeService>(() => memberTypeService);
|
||||
_memberService = new Lazy<IMemberService>(() => memberService);
|
||||
_userService = new Lazy<IUserService>(() => userService);
|
||||
|
||||
//_sectionService = new Lazy<ISectionService>(() => sectionService);
|
||||
//_treeService = new Lazy<IApplicationTreeService>(() => treeService);
|
||||
}
|
||||
@@ -113,13 +121,13 @@ namespace Umbraco.Core.Services
|
||||
_userService = new Lazy<IUserService>(() => new UserService(provider, repositoryFactory.Value));
|
||||
|
||||
if (_memberService == null)
|
||||
_memberService = new Lazy<IMemberService>(() => new MemberService(provider, repositoryFactory.Value, _memberGroupService.Value));
|
||||
_memberService = new Lazy<IMemberService>(() => new MemberService(provider, repositoryFactory.Value, _memberGroupService.Value, _dataTypeService.Value));
|
||||
|
||||
if (_contentService == null)
|
||||
_contentService = new Lazy<IContentService>(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy));
|
||||
_contentService = new Lazy<IContentService>(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy, _dataTypeService.Value));
|
||||
|
||||
if (_mediaService == null)
|
||||
_mediaService = new Lazy<IMediaService>(() => new MediaService(provider, repositoryFactory.Value));
|
||||
_mediaService = new Lazy<IMediaService>(() => new MediaService(provider, repositoryFactory.Value, _dataTypeService.Value));
|
||||
|
||||
if (_contentTypeService == null)
|
||||
_contentTypeService = new Lazy<IContentTypeService>(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value));
|
||||
|
||||
@@ -1123,17 +1123,18 @@ namespace Umbraco.Core
|
||||
/// <returns>Updated string</returns>
|
||||
public static string Replace(this string source, string oldString, string newString, StringComparison stringComparison)
|
||||
{
|
||||
var index = source.IndexOf(oldString, stringComparison);
|
||||
// This initialisation ensures the first check starts at index zero of the source. On successive checks for
|
||||
// a match, the source is skipped to immediately after the last replaced occurrence for efficiency
|
||||
// and to avoid infinite loops when oldString and newString compare equal.
|
||||
int index = -1 * newString.Length;
|
||||
|
||||
// Determine if we found a match
|
||||
var matchFound = index >= 0;
|
||||
|
||||
if (matchFound)
|
||||
// Determine if there are any matches left in source, starting from just after the result of replacing the last match.
|
||||
while((index = source.IndexOf(oldString, index + newString.Length, stringComparison)) >= 0)
|
||||
{
|
||||
// Remove the old text
|
||||
// Remove the old text.
|
||||
source = source.Remove(index, oldString.Length);
|
||||
|
||||
// Add the replacemenet text
|
||||
// Add the replacemenet text.
|
||||
source = source.Insert(index, newString);
|
||||
}
|
||||
|
||||
@@ -1180,5 +1181,24 @@ namespace Umbraco.Core
|
||||
*/
|
||||
}
|
||||
|
||||
public static bool ContainsAny(this string haystack, IEnumerable<string> needles, StringComparison comparison = StringComparison.CurrentCulture)
|
||||
{
|
||||
if (haystack == null) throw new ArgumentNullException("haystack");
|
||||
if (string.IsNullOrEmpty(haystack) == false || needles.Any())
|
||||
{
|
||||
return needles.Any(value => haystack.IndexOf(value) >= 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CsvContains(this string csv, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(csv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var idCheckList = csv.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return idCheckList.Contains(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ namespace Umbraco.Core.Strings
|
||||
/// <summary>
|
||||
/// Umbraco "safe alias" case.
|
||||
/// </summary>
|
||||
/// <remarks>This is for backward compatibility. Casing is unchanged within terms,
|
||||
/// and is pascal otherwise.</remarks>
|
||||
/// <remarks>Uppercases the first char of each term except for the first
|
||||
/// char of the string, everything else including the first char of the
|
||||
/// string is unchanged.</remarks>
|
||||
UmbracoCase = 0x20,
|
||||
|
||||
|
||||
@@ -91,7 +92,7 @@ namespace Umbraco.Core.Strings
|
||||
/// <summary>
|
||||
/// Flag mask for role.
|
||||
/// </summary>
|
||||
RoleMask = UrlSegment | Alias | FileName | ConvertCase,
|
||||
RoleMask = UrlSegment | Alias | UnderscoreAlias | FileName | ConvertCase,
|
||||
|
||||
/// <summary>
|
||||
/// Url role.
|
||||
@@ -111,6 +112,12 @@ namespace Umbraco.Core.Strings
|
||||
/// <summary>
|
||||
/// ConvertCase role.
|
||||
/// </summary>
|
||||
ConvertCase = 0x080000
|
||||
ConvertCase = 0x080000,
|
||||
|
||||
/// <summary>
|
||||
/// UnderscoreAlias role.
|
||||
/// </summary>
|
||||
/// <remarks>This is Alias + leading underscore.</remarks>
|
||||
UnderscoreAlias = 0x100000
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user