Merge branch 'v8/dev' of https://github.com/umbraco/Umbraco-CMS into feature/backoffice-segment-support-server

This commit is contained in:
Daniël Knippers
2019-11-22 14:06:10 +01:00
577 changed files with 12683 additions and 7646 deletions

View File

@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.3.0")]
[assembly: AssemblyInformationalVersion("8.3.0")]
[assembly: AssemblyFileVersion("8.6.0")]
[assembly: AssemblyInformationalVersion("8.6.0")]

View File

@@ -11,5 +11,6 @@
public const string TemplateFrontEndCacheKey = "template";
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
public const string MacroFromAliasCacheKey = "macroFromAlias_";
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Collections
{
@@ -26,7 +27,7 @@ namespace Umbraco.Core.Collections
/// <param name="equalityComparer">The equality comparer to use when comparing keys, or null to use the default comparer.</param>
public ObservableDictionary(Func<TValue, TKey> keySelector, IEqualityComparer<TKey> equalityComparer = null)
{
KeySelector = keySelector ?? throw new ArgumentException("keySelector");
KeySelector = keySelector ?? throw new ArgumentException(nameof(keySelector));
Indecies = new Dictionary<TKey, int>(equalityComparer);
}
@@ -36,7 +37,7 @@ namespace Umbraco.Core.Collections
{
var key = KeySelector(item);
if (Indecies.ContainsKey(key))
throw new DuplicateKeyException(key.ToString());
throw new ArgumentException($"An element with the same key '{key}' already exists in the dictionary.", nameof(item));
if (index != Count)
{
@@ -91,7 +92,7 @@ namespace Umbraco.Core.Collections
{
//confirm key matches
if (!KeySelector(value).Equals(key))
throw new InvalidOperationException("Key of new value does not match");
throw new InvalidOperationException("Key of new value does not match.");
if (!Indecies.ContainsKey(key))
{
@@ -118,7 +119,7 @@ namespace Umbraco.Core.Collections
//confirm key matches
if (!KeySelector(value).Equals(key))
throw new InvalidOperationException("Key of new value does not match");
throw new InvalidOperationException("Key of new value does not match.");
this[Indecies[key]] = value;
return true;
@@ -155,12 +156,12 @@ namespace Umbraco.Core.Collections
{
if (!Indecies.ContainsKey(currentKey))
{
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
throw new InvalidOperationException($"No item with the key '{currentKey}' was found in the dictionary.");
}
if (ContainsKey(newKey))
{
throw new DuplicateKeyException(newKey.ToString());
throw new ArgumentException($"An element with the same key '{newKey}' already exists in the dictionary.", nameof(newKey));
}
var currentIndex = Indecies[currentKey];
@@ -234,16 +235,5 @@ namespace Umbraco.Core.Collections
}
#endregion
internal class DuplicateKeyException : Exception
{
public DuplicateKeyException(string key)
: base("Attempted to insert duplicate key \"" + key + "\" in collection.")
{
Key = key;
}
public string Key { get; }
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.Serialization;
using System.Text;
namespace Umbraco.Core.Composing.LightInject
@@ -6,20 +7,51 @@ namespace Umbraco.Core.Composing.LightInject
/// <summary>
/// Represents errors that occur due to LightInject.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class LightInjectException : Exception
{
public LightInjectException(string message)
: base(message)
{ }
public LightInjectException(string message, Exception innerException)
: base(message, innerException)
{ }
private const string LightInjectUnableToResolveType = "Unable to resolve type:";
private const string LightInjectUnresolvedDependency = "Unresolved dependency ";
private const string LightInjectRequestedDependency = "[Requested dependency:";
/// <summary>
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
/// </summary>
public LightInjectException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public LightInjectException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public LightInjectException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LightInjectException"/> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected LightInjectException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
/// <summary>
/// Tries to throw the exception with additional details.
/// </summary>
/// <param name="e">The exception.</param>
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
public static void TryThrow(Exception e)
{
var ex = e as InvalidOperationException;
@@ -32,6 +64,12 @@ namespace Umbraco.Core.Composing.LightInject
throw new LightInjectException(sb.ToString(), e);
}
/// <summary>
/// Tries to throw the exception with additional details.
/// </summary>
/// <param name="e">The exception.</param>
/// <param name="implementingType">The implementing type.</param>
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
public static void TryThrow(Exception e, Type implementingType)
{
var ex = e as InvalidOperationException;
@@ -45,6 +83,11 @@ namespace Umbraco.Core.Composing.LightInject
throw new LightInjectException(sb.ToString(), e);
}
/// <summary>
/// Writes the details.
/// </summary>
/// <param name="ex">The exception.</param>
/// <param name="sb">The <see cref="StringBuilder" /> to write the details to.</param>
private static void WriteDetails(InvalidOperationException ex, StringBuilder sb)
{
ex = ex.InnerException as InvalidOperationException;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Web;
@@ -813,11 +814,44 @@ namespace Umbraco.Core.Composing
}
/// <summary>
/// Represents the error that occurs when a type was not found in the cache type
/// list with the specified TypeResolutionKind.
/// Represents the error that occurs when a type was not found in the cache type list with the specified TypeResolutionKind.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
internal class CachedTypeNotFoundInFileException : Exception
{ }
{
/// <summary>
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
/// </summary>
public CachedTypeNotFoundInFileException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public CachedTypeNotFoundInFileException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public CachedTypeNotFoundInFileException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected CachedTypeNotFoundInFileException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
#endregion
}

View File

@@ -6,6 +6,7 @@ namespace Umbraco.Core.Configuration.Grid
public interface IGridEditorConfig
{
string Name { get; }
string NameTemplate { get; }
string Alias { get; }
string View { get; }
string Render { get; }

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ContentElement : UmbracoConfigurationElement, IContentSection
{
private const string DefaultPreviewBadge = @"<a id=""umbracoPreviewBadge"" style=""z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""#"" OnClick=""javascript:window.top.location.href = '{0}/preview/end?redir={1}'""><span style=""display:none;"">In Preview Mode - click to end</span></a>";
private const string DefaultPreviewBadge = @"<div id=""umbracoPreviewBadge"" class=""umbraco-preview-badge""><span class=""umbraco-preview-badge__header"">Preview mode</span><a href=""{0}/preview/end?redir={1}"" class=""umbraco-preview-badge__end""><svg viewBox=""0 0 100 100"" xmlns=""http://www.w3.org/2000/svg""><title>Click to end</title><path d=""M5273.1 2400.1v-2c0-2.8-5-4-9.7-4s-9.7 1.3-9.7 4v2a7 7 0 002 4.9l5 4.9c.3.3.4.6.4 1v6.4c0 .4.2.7.6.8l2.9.9c.5.1 1-.2 1-.8v-7.2c0-.4.2-.7.4-1l5.1-5a7 7 0 002-4.9zm-9.7-.1c-4.8 0-7.4-1.3-7.5-1.8.1-.5 2.7-1.8 7.5-1.8s7.3 1.3 7.5 1.8c-.2.5-2.7 1.8-7.5 1.8z""/><path d=""M5268.4 2410.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1h-4.3zM5272.7 2413.7h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1zM5272.7 2417h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1 0-.5-.4-1-1-1z""/><path d=""M78.2 13l-8.7 11.7a32.5 32.5 0 11-51.9 25.8c0-10.3 4.7-19.7 12.9-25.8L21.8 13a47 47 0 1056.4 0z""/><path d=""M42.7 2.5h14.6v49.4H42.7z""/></svg></a></div><style type=""text/css"">.umbraco-preview-badge {{position: absolute;top: 1em;right: 1em;display: inline-flex;background: #1b264f;color: #fff;padding: 1em;font-size: 12px;z-index: 99999999;justify-content: center;align-items: center;box-shadow: 0 10px 50px rgba(0, 0, 0, .1), 0 6px 20px rgba(0, 0, 0, .16);line-height: 1;}}.umbraco-preview-badge__header {{font-weight: bold;}}.umbraco-preview-badge__end {{width: 3em;padding: 1em;margin: -1em -1em -1em 2em;display: flex;flex-shrink: 0;align-items: center;align-self: stretch;}}.umbraco-preview-badge__end:hover,.umbraco-preview-badge__end:focus {{background: #f5c1bc;}}.umbraco-preview-badge__end svg {{fill: #fff;width:1em;}}</style>";
[ConfigurationProperty("imaging")]
internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"];

View File

@@ -221,7 +221,8 @@ namespace Umbraco.Core
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
Name = FailedPasswordAttemptsLabel,
DataTypeId = Constants.DataTypes.LabelInt
}
},
{
@@ -242,35 +243,40 @@ namespace Umbraco.Core
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
Name = LastLockoutDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
Name = LastLoginDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
Name = LastPasswordChangeDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
Name = PasswordAnswerLabel,
DataTypeId = Constants.DataTypes.LabelString
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
Name = PasswordQuestionLabel,
DataTypeId = Constants.DataTypes.LabelString
}
}
};

View File

@@ -85,12 +85,7 @@ namespace Umbraco.Core
/// ListView.
/// </summary>
public const string ListView = "Umbraco.ListView";
/// <summary>
/// Macro Container.
/// </summary>
public const string MacroContainer = "Umbraco.MacroContainer";
/// <summary>
/// Media Picker.
/// </summary>

View File

@@ -1,16 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// The exception that is thrown when a null reference, or an empty argument,
/// is passed to a method that does not accept it as a valid argument.
/// The exception that is thrown when a null reference, or an empty argument, is passed to a method that does not accept it as a valid argument.
/// </summary>
/// <seealso cref="System.ArgumentNullException" />
[Obsolete("Throw an ArgumentNullException when the parameter is null or an ArgumentException when its empty instead.")]
[Serializable]
public class ArgumentNullOrEmptyException : ArgumentNullException
{
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class
/// with the name of the parameter that caused this exception.
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
/// </summary>
public ArgumentNullOrEmptyException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with the name of the parameter that caused this exception.
/// </summary>
/// <param name="paramName">The named of the parameter that caused the exception.</param>
public ArgumentNullOrEmptyException(string paramName)
@@ -18,13 +26,30 @@ namespace Umbraco.Core.Exceptions
{ }
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class
/// with a specified error message and the name of the parameter that caused this exception.
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with a specified error message and the name of the parameter that caused this exception.
/// </summary>
/// <param name="paramName">The named of the parameter that caused the exception.</param>
/// <param name="message">A message that describes the error.</param>
public ArgumentNullOrEmptyException(string paramName, string message)
: base(paramName, message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for this exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public ArgumentNullOrEmptyException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">An object that describes the source or destination of the serialized data.</param>
protected ArgumentNullOrEmptyException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -1,14 +1,45 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// The exception that is thrown when authorization failed.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class AuthorizationException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
/// </summary>
public AuthorizationException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public AuthorizationException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public AuthorizationException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected AuthorizationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.Serialization;
using System.Text;
namespace Umbraco.Core.Exceptions
@@ -6,6 +7,8 @@ namespace Umbraco.Core.Exceptions
/// <summary>
/// An exception that is thrown if the Umbraco application cannot boot.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class BootFailedException : Exception
{
/// <summary>
@@ -14,27 +17,47 @@ namespace Umbraco.Core.Exceptions
public const string DefaultMessage = "Boot failed: Umbraco cannot run. See Umbraco's log file for more details.";
/// <summary>
/// Initializes a new instance of the <see cref="Exception"/> class with a specified error message.
/// Initializes a new instance of the <see cref="BootFailedException" /> class.
/// </summary>
/// <param name="message">The message that describes the error. </param>
public BootFailedException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Exception" /> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public BootFailedException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Exception"/> class with a specified error message
/// Initializes a new instance of the <see cref="Exception" /> class with a specified error message
/// and a reference to the inner exception which is the cause of this exception.
/// </summary>
/// <param name="message">The message that describes the error. </param>
/// <param name="inner">The inner exception, or null.</param>
public BootFailedException(string message, Exception inner)
: base(message, inner)
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The inner exception, or null.</param>
public BootFailedException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Rethrows a captured <see cref="BootFailedException"/>.
/// Initializes a new instance of the <see cref="BootFailedException" /> class.
/// </summary>
/// <remarks>The exception can be null, in which case a default message is used.</remarks>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected BootFailedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
/// <summary>
/// Rethrows a captured <see cref="BootFailedException" />.
/// </summary>
/// <param name="bootFailedException">The boot failed exception.</param>
/// <exception cref="Umbraco.Core.Exceptions.BootFailedException">
/// </exception>
/// <remarks>
/// The exception can be null, in which case a default message is used.
/// </remarks>
public static void Rethrow(BootFailedException bootFailedException)
{
if (bootFailedException == null)

View File

@@ -1,12 +0,0 @@
using System;
namespace Umbraco.Core.Exceptions
{
internal class ConnectionException : Exception
{
public ConnectionException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@@ -1,21 +1,98 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <seealso cref="System.Exception" />
[Serializable]
internal class DataOperationException<T> : Exception
where T : Enum
{
/// <summary>
/// Gets the operation.
/// </summary>
/// <value>
/// The operation.
/// </value>
/// <remarks>
/// This object should be serializable to prevent a <see cref="SerializationException" /> to be thrown.
/// </remarks>
public T Operation { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
public DataOperationException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public DataOperationException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public DataOperationException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
/// <param name="operation">The operation.</param>
public DataOperationException(T operation)
: this(operation, "Data operation exception: " + operation)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="message">The message.</param>
public DataOperationException(T operation, string message)
: base(message)
{
Operation = operation;
}
public DataOperationException(T operation)
: base("Data operation exception: " + operation)
/// <summary>
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
/// <exception cref="ArgumentNullException">info</exception>
protected DataOperationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Operation = operation;
Operation = (T)Enum.Parse(typeof(T), info.GetString(nameof(Operation)));
}
/// <summary>
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
/// <exception cref="ArgumentNullException">info</exception>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
info.AddValue(nameof(Operation), Enum.GetName(typeof(T), Operation));
base.GetObjectData(info, context);
}
}
}

View File

@@ -1,44 +1,126 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// The exception that is thrown when a composition is invalid.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class InvalidCompositionException : Exception
{
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
{
ContentTypeAlias = contentTypeAlias;
AddedCompositionAlias = addedCompositionAlias;
PropertyTypeAliases = propertyTypeAliass;
}
/// <summary>
/// Gets the content type alias.
/// </summary>
/// <value>
/// The content type alias.
/// </value>
public string ContentTypeAlias { get; }
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
{
ContentTypeAlias = contentTypeAlias;
PropertyTypeAliases = propertyTypeAliass;
}
/// <summary>
/// Gets the added composition alias.
/// </summary>
/// <value>
/// The added composition alias.
/// </value>
public string AddedCompositionAlias { get; }
public string ContentTypeAlias { get; private set; }
/// <summary>
/// Gets the property type aliases.
/// </summary>
/// <value>
/// The property type aliases.
/// </value>
public string[] PropertyTypeAliases { get; }
public string AddedCompositionAlias { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
public InvalidCompositionException()
{ }
public string[] PropertyTypeAliases { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
/// <param name="contentTypeAlias">The content type alias.</param>
/// <param name="propertyTypeAliases">The property type aliases.</param>
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliases)
: this(contentTypeAlias, null, propertyTypeAliases)
{ }
public override string Message
{
get
{
return AddedCompositionAlias.IsNullOrWhiteSpace()
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
/// <param name="contentTypeAlias">The content type alias.</param>
/// <param name="addedCompositionAlias">The added composition alias.</param>
/// <param name="propertyTypeAliases">The property type aliases.</param>
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliases)
: this(addedCompositionAlias.IsNullOrWhiteSpace()
? string.Format(
"ContentType with alias '{0}' has an invalid composition " +
"and there was a conflict on the following PropertyTypes: '{1}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
contentTypeAlias, string.Join(", ", propertyTypeAliases))
: string.Format(
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
"but there was a conflict on the following PropertyTypes: '{2}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
addedCompositionAlias, contentTypeAlias, string.Join(", ", propertyTypeAliases)))
{
ContentTypeAlias = contentTypeAlias;
AddedCompositionAlias = addedCompositionAlias;
PropertyTypeAliases = propertyTypeAliases;
}
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public InvalidCompositionException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public InvalidCompositionException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected InvalidCompositionException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
ContentTypeAlias = info.GetString(nameof(ContentTypeAlias));
AddedCompositionAlias = info.GetString(nameof(AddedCompositionAlias));
PropertyTypeAliases = (string[])info.GetValue(nameof(PropertyTypeAliases), typeof(string[]));
}
/// <summary>
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
/// <exception cref="ArgumentNullException">info</exception>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
info.AddValue(nameof(ContentTypeAlias), ContentTypeAlias);
info.AddValue(nameof(AddedCompositionAlias), AddedCompositionAlias);
info.AddValue(nameof(PropertyTypeAliases), PropertyTypeAliases);
base.GetObjectData(info, context);
}
}
}

View File

@@ -4,25 +4,42 @@ using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// Internal exception that in theory should never ben thrown, it is only thrown in circumstances that should never happen
/// Represents an internal exception that in theory should never been thrown, it is only thrown in circumstances that should never happen.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
internal class PanicException : Exception
public class PanicException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="PanicException" /> class.
/// </summary>
public PanicException()
{
}
{ }
public PanicException(string message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PanicException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public PanicException(string message)
: base(message)
{ }
public PanicException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PanicException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public PanicException(string message, Exception innerException)
: base(message, innerException)
{ }
protected PanicException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PanicException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected PanicException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -1,27 +1,52 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// The exception that is thrown when a requested method or operation is not, and will not be, implemented.
/// </summary>
/// <remarks>The <see cref="NotImplementedException"/> is to be used when some code is not implemented,
/// <remarks>
/// The <see cref="NotImplementedException" /> is to be used when some code is not implemented,
/// but should eventually be implemented (i.e. work in progress) and is reported by tools such as ReSharper.
/// This exception is to be used when some code is not implemented, and is not meant to be, for whatever
/// reason.</remarks>
/// reason.
/// </remarks>
/// <seealso cref="System.NotImplementedException" />
[Serializable]
[Obsolete("If a method or operation is not, and will not be, implemented, it is invalid or not supported, so we should throw either an InvalidOperationException or NotSupportedException instead.")]
public class WontImplementException : NotImplementedException
{
/// <summary>
/// Initializes a new instance of the <see cref="WontImplementException"/> class.
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
/// </summary>
public WontImplementException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="WontImplementException"/> class with a specified reason message.
/// Initializes a new instance of the <see cref="WontImplementException" /> class with a specified reason message.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public WontImplementException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="inner">The exception that is the cause of the current exception. If the <paramref name="inner" /> parameter is not <see langword="null" />, the current exception is raised in a <see langword="catch" /> block that handles the inner exception.</param>
public WontImplementException(string message, Exception inner)
: base(message, inner)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected WontImplementException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -1,20 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
namespace Umbraco.Core.IO
{
/// <summary>
/// The exception that is thrown when the caller does not have the required permission to access a file.
/// </summary>
/// <seealso cref="System.Exception" />
[Obsolete("Throw an UnauthorizedAccessException instead.")]
[Serializable]
public class FileSecurityException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
/// </summary>
public FileSecurityException()
{
{ }
}
/// <summary>
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public FileSecurityException(string message)
: base(message)
{ }
public FileSecurityException(string message) : base(message)
{
/// <summary>
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public FileSecurityException(string message, Exception innerException)
: base(message, innerException)
{ }
}
/// <summary>
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected FileSecurityException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Media;
using Umbraco.Core.Models;
namespace Umbraco.Core.IO
@@ -92,7 +87,8 @@ namespace Umbraco.Core.IO
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType));
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentNullOrEmptyException(nameof(filename));
if (filename == null) throw new ArgumentNullException(nameof(filename));
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename));
if (filestream == null) throw new ArgumentNullException(nameof(filestream));
// clear the old file, if any
@@ -111,7 +107,8 @@ namespace Umbraco.Core.IO
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType));
if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentNullOrEmptyException(nameof(sourcepath));
if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath));
if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath));
// ensure we have a file to copy
if (FileExists(sourcepath) == false) return null;

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using System.Threading;
using Umbraco.Core.Logging;
@@ -39,9 +38,11 @@ namespace Umbraco.Core.IO
public PhysicalFileSystem(string rootPath, string rootUrl)
{
if (string.IsNullOrEmpty(rootPath)) throw new ArgumentNullOrEmptyException(nameof(rootPath));
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentNullOrEmptyException(nameof(rootUrl));
if (rootPath.StartsWith("~/")) throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'");
if (rootPath == null) throw new ArgumentNullException(nameof(rootPath));
if (string.IsNullOrEmpty(rootPath)) throw new ArgumentException("Value can't be empty.", nameof(rootPath));
if (rootUrl == null) throw new ArgumentNullException(nameof(rootUrl));
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl));
if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath));
// rootPath should be... rooted, as in, it's a root path!
if (Path.IsPathRooted(rootPath) == false)
@@ -314,7 +315,7 @@ namespace Umbraco.Core.IO
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root.");
throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root.");
}
/// <summary>

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Web.Hosting;
using Umbraco.Core.Logging;
@@ -65,7 +65,7 @@ namespace Umbraco.Core
// a new process for the same application path
var appPath = HostingEnvironment.ApplicationPhysicalPath;
var hash = (appId + ":::" + appPath).ToSHA1();
var hash = (appId + ":::" + appPath).GenerateHash<SHA1>();
var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK";
_asyncLock = new AsyncLock(lockName);

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Umbraco.Core.Cache;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors;
@@ -42,7 +41,8 @@ namespace Umbraco.Core.Manifest
_cache = appCaches.RuntimeCache;
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
_filters = filters ?? throw new ArgumentNullException(nameof(filters));
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullOrEmptyException(nameof(path));
if (path == null) throw new ArgumentNullException(nameof(path));
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path));
Path = path;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -155,8 +155,8 @@ namespace Umbraco.Core.Manifest
/// </summary>
internal PackageManifest ParseManifest(string text)
{
if (string.IsNullOrWhiteSpace(text))
throw new ArgumentNullOrEmptyException(nameof(text));
if (text == null) throw new ArgumentNullException(nameof(text));
if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text));
var manifest = JsonConvert.DeserializeObject<PackageManifest>(text,
new DataEditorConverter(_logger),

View File

@@ -343,16 +343,20 @@ namespace Umbraco.Core.Mapping
if (ctor == null) return null;
if (_ctors.ContainsKey(sourceType))
_ctors.AddOrUpdate(sourceType, sourceCtor, (k, v) =>
{
// Add missing constructors
foreach (var c in sourceCtor)
{
if (!_ctors[sourceType].TryGetValue(c.Key, out _))
_ctors[sourceType].Add(c.Key, c.Value);
}
}
else
_ctors[sourceType] = sourceCtor;
if (!v.ContainsKey(c.Key))
{
v.Add(c.Key, c.Value);
}
}
return v;
});
return ctor;
}

View File

@@ -1,22 +0,0 @@
using System;
namespace Umbraco.Core.Migrations
{
/// <summary>
/// Used if a migration has executed but the whole process has failed and cannot be rolled back
/// </summary>
internal class DataLossException : Exception
{
public DataLossException(string msg)
: base(msg)
{
}
public DataLossException(string msg, Exception inner)
: base(msg, inner)
{
}
}
}

View File

@@ -1,4 +1,4 @@
using Umbraco.Core.Exceptions;
using System;
using Umbraco.Core.Migrations.Expressions.Common;
using Umbraco.Core.Migrations.Expressions.Delete.Column;
using Umbraco.Core.Migrations.Expressions.Delete.Constraint;
@@ -39,8 +39,9 @@ namespace Umbraco.Core.Migrations.Expressions.Delete
/// <inheritdoc />
public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true)
{
if (tableName.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(tableName));
if (tableName == null) throw new ArgumentNullException(nameof(tableName));
if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(tableName));
return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeleteLocal = local, DeleteForeign = foreign };
}

View File

@@ -1,28 +1,49 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Migrations
{
/// <summary>
/// Represents errors that occurs when a migration exception is not executed.
/// The exception that is thrown when a migration expression is not executed.
/// </summary>
/// <remarks>
/// <para>Migration expression such as Alter.Table(...).Do() *must* end with Do() else they are
/// not executed. When a non-executed expression is detected, an IncompleteMigrationExpressionException
/// is thrown.</para>
/// Migration expressions such as Alter.Table(...).Do() must end with Do(), else they are not executed.
/// When a non-executed expression is detected, an IncompleteMigrationExpressionException is thrown.
/// </remarks>
/// <seealso cref="System.Exception" />
[Serializable]
public class IncompleteMigrationExpressionException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException"/> class.
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException" /> class.
/// </summary>
public IncompleteMigrationExpressionException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException"/> class with a message.
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException" /> class with a message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public IncompleteMigrationExpressionException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public IncompleteMigrationExpressionException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected IncompleteMigrationExpressionException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View File

@@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.Upgrade;
@@ -278,8 +277,10 @@ namespace Umbraco.Core.Migrations.Install
/// <param name="logger">A logger.</param>
private static void SaveConnectionString(string connectionString, string providerName, ILogger logger)
{
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString));
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName));
if (connectionString == null) throw new ArgumentNullException(nameof(connectionString));
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionString));
if (providerName == null) throw new ArgumentNullException(nameof(providerName));
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(providerName));
var fileSource = "web.config";
var fileName = IOHelper.MapPath(SystemDirectories.Root +"/" + fileSource);

View File

@@ -227,8 +227,8 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
using Type = System.Type;
@@ -25,7 +24,9 @@ namespace Umbraco.Core.Migrations
/// <param name="name">The name of the plan.</param>
public MigrationPlan(string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
Name = name;
}
@@ -43,7 +44,8 @@ namespace Umbraco.Core.Migrations
private MigrationPlan Add(string sourceState, string targetState, Type migration)
{
if (sourceState == null) throw new ArgumentNullException(nameof(sourceState));
if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentNullOrEmptyException(nameof(targetState));
if (targetState == null) throw new ArgumentNullException(nameof(targetState));
if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(targetState));
if (sourceState == targetState) throw new ArgumentException("Source and target state cannot be identical.");
if (migration == null) throw new ArgumentNullException(nameof(migration));
if (!migration.Implements<IMigration>()) throw new ArgumentException($"Type {migration.Name} does not implement IMigration.", nameof(migration));
@@ -135,10 +137,12 @@ namespace Umbraco.Core.Migrations
/// </summary>
public MigrationPlan ToWithClone(string startState, string endState, string targetState)
{
if (string.IsNullOrWhiteSpace(startState)) throw new ArgumentNullOrEmptyException(nameof(startState));
if (string.IsNullOrWhiteSpace(endState)) throw new ArgumentNullOrEmptyException(nameof(endState));
if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentNullOrEmptyException(nameof(targetState));
if (startState == null) throw new ArgumentNullException(nameof(startState));
if (string.IsNullOrWhiteSpace(startState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(startState));
if (endState == null) throw new ArgumentNullException(nameof(endState));
if (string.IsNullOrWhiteSpace(endState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(endState));
if (targetState == null) throw new ArgumentNullException(nameof(targetState));
if (string.IsNullOrWhiteSpace(targetState)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(targetState));
if (startState == endState) throw new ArgumentException("Start and end states cannot be identical.");
startState = startState.Trim();
@@ -317,7 +321,7 @@ namespace Umbraco.Core.Migrations
// throw a raw exception here: this should never happen as the plan has
// been validated - this is just a paranoid safety test
if (!_transitions.TryGetValue(origState, out transition))
throw new Exception($"Unknown state \"{origState}\".");
throw new InvalidOperationException($"Unknown state \"{origState}\".");
}
// prepare and de-duplicate post-migrations, only keeping the 1st occurence
@@ -339,7 +343,7 @@ namespace Umbraco.Core.Migrations
// safety check - again, this should never happen as the plan has been validated,
// and this is just a paranoid safety test
if (origState != _finalState)
throw new Exception($"Internal error, reached state {origState} which is not final state {_finalState}");
throw new InvalidOperationException($"Internal error, reached state {origState} which is not final state {_finalState}");
return origState;
}
@@ -358,7 +362,7 @@ namespace Umbraco.Core.Migrations
var states = new List<string> { origState };
if (!_transitions.TryGetValue(origState, out var transition))
throw new Exception($"Unknown state \"{origState}\".");
throw new InvalidOperationException($"Unknown state \"{origState}\".");
while (transition != null)
{
@@ -373,12 +377,12 @@ namespace Umbraco.Core.Migrations
}
if (!_transitions.TryGetValue(origState, out transition))
throw new Exception($"Unknown state \"{origState}\".");
throw new InvalidOperationException($"Unknown state \"{origState}\".");
}
// safety check
if (origState != (toState ?? _finalState))
throw new Exception($"Internal error, reached state {origState} which is not state {toState ?? _finalState}");
throw new InvalidOperationException($"Internal error, reached state {origState} which is not state {toState ?? _finalState}");
return states;
}
@@ -417,7 +421,7 @@ namespace Umbraco.Core.Migrations
public override string ToString()
{
return MigrationType == typeof(NoopMigration)
? $"{(SourceState == "" ? "<empty>" : SourceState)} --> {TargetState}"
? $"{(SourceState == string.Empty ? "<empty>" : SourceState)} --> {TargetState}"
: $"{SourceState} -- ({MigrationType.FullName}) --> {TargetState}";
}
}

View File

@@ -6,6 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_1;
using Umbraco.Core.Migrations.Upgrade.V_8_1_0;
using Umbraco.Core.Migrations.Upgrade.V_8_6_0;
namespace Umbraco.Core.Migrations.Upgrade
{
@@ -182,6 +183,9 @@ namespace Umbraco.Core.Migrations.Upgrade
To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}");
To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}");
// to 8.6.0
To<AddPropertyTypeValidationMessageColumns>("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}");
//FINAL
}
}

View File

@@ -74,9 +74,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
.From<DataTypeDto>()
.Where<DataTypeDto>(x => x.NodeId == group.Key)).First();
// check for duplicate aliases
var aliases = group.Select(x => x.Alias).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
if (aliases.Distinct().Count() != aliases.Length)
throw new InvalidOperationException($"Cannot migrate prevalues for datatype id={dataType.NodeId}, editor={dataType.EditorAlias}: duplicate alias.");
// handle null/empty aliases
int index = 0;
var dictionary = group.ToDictionary(x => string.IsNullOrWhiteSpace(x.Alias) ? index++.ToString() : x.Alias);
// migrate the preValues to configuration
var migrator = _preValueMigrators.GetMigrator(dataType.EditorAlias) ?? new DefaultPreValueMigrator();
var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, group.ToDictionary(x => x.Alias, x => x));
var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, dictionary);
var json = JsonConvert.SerializeObject(config);
// validate - and kill the migration if it fails

View File

@@ -24,8 +24,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
}
// assuming we don't want to fall back to array
if (aliases.Length != preValuesA.Count || aliases.Any(string.IsNullOrWhiteSpace))
throw new InvalidOperationException($"Cannot migrate datatype w/ id={dataTypeId} preValues: duplicate or null/empty alias.");
if (aliases.Any(string.IsNullOrWhiteSpace))
throw new InvalidOperationException($"Cannot migrate prevalues for datatype id={dataTypeId}, editor={editorAlias}: null/empty alias.");
// dictionary-base prevalues
return GetPreValues(preValuesA).ToDictionary(x => x.Alias, GetPreValueValue);

View File

@@ -0,0 +1,20 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
{
public class AddPropertyTypeValidationMessageColumns : MigrationBase
{
public AddPropertyTypeValidationMessageColumns(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
AddColumnIfNotExists<PropertyTypeDto>(columns, "mandatoryMessage");
AddColumnIfNotExists<PropertyTypeDto>(columns, "validationRegExpMessage");
}
}
}

View File

@@ -4,8 +4,6 @@ using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
@@ -229,8 +227,8 @@ namespace Umbraco.Core.Models
private void ClearCultureInfo(string culture)
{
if (culture.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
if (_cultureInfos == null) return;
_cultureInfos.Remove(culture);

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
@@ -18,7 +17,9 @@ namespace Umbraco.Core.Models
/// </summary>
public ContentCultureInfos(string culture)
{
if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
Culture = culture;
}

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Umbraco.Core.Collections;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models
{
@@ -23,7 +21,9 @@ namespace Umbraco.Core.Models
/// </summary>
public void AddOrUpdate(string culture, string name, DateTime date)
{
if (culture.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
culture = culture.ToLowerInvariant();
if (TryGetValue(culture, out var item))

View File

@@ -67,6 +67,12 @@ namespace Umbraco.Core.Models.ContentEditing
/// </remarks>
[DataMember(Name = "active")]
public bool Active { get; set; }
/// <summary>
/// Gets or sets the content app badge.
/// </summary>
[DataMember(Name = "badge")]
public ContentAppBadge Badge { get; set; }
}
}

View File

@@ -0,0 +1,39 @@
namespace Umbraco.Core.Models.ContentEditing
{
using System.Runtime.Serialization;
using Umbraco.Core.Events;
/// <summary>
/// Represents a content app badge
/// </summary>
[DataContract(Name = "badge", Namespace = "")]
public class ContentAppBadge
{
/// <summary>
/// Initializes a new instance of the <see cref="ContentAppBadge"/> class.
/// </summary>
public ContentAppBadge()
{
this.Type = ContentAppBadgeType.Default;
}
/// <summary>
/// Gets or sets the number displayed in the badge
/// </summary>
[DataMember(Name = "count")]
public int Count { get; set; }
/// <summary>
/// Gets or sets the type of badge to display
/// </summary>
/// <remarks>
/// <para>This controls the background color of the badge.</para>
/// <para>Warning will display a dark yellow badge</para>
/// <para>Alert will display a red badge</para>
/// <para>Default will display a turquoise badge</para>
/// </remarks>
[DataMember(Name = "type")]
public ContentAppBadgeType Type { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
namespace Umbraco.Core.Models.ContentEditing
{
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
/// <summary>
/// Represent the content app badge types
/// </summary>
[DataContract(Name = "contentAppBadgeType")]
[JsonConverter(typeof(StringEnumConverter))]
public enum ContentAppBadgeType
{
[EnumMember(Value = "default")]
Default = 0,
[EnumMember(Value = "warning")]
Warning = 1,
[EnumMember(Value = "alert")]
Alert = 2
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models
{
@@ -103,11 +102,11 @@ namespace Umbraco.Core.Models
public static void SetPublishInfo(this IContent content, string culture, string name, DateTime date)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (culture.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
content.PublishCultureInfos.AddOrUpdate(culture, name, date);
}
@@ -153,11 +152,11 @@ namespace Umbraco.Core.Models
public static void SetCultureInfo(this IContentBase content, string culture, string name, DateTime date)
{
if (name.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (culture.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
content.CultureInfos.AddOrUpdate(culture, name, date);
}
@@ -276,8 +275,8 @@ namespace Umbraco.Core.Models
/// <returns></returns>
public static bool ClearPublishInfo(this IContent content, string culture)
{
if (culture.IsNullOrWhiteSpace())
throw new ArgumentNullOrEmptyException(nameof(culture));
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(culture));
var removed = content.PublishCultureInfos.Remove(culture);
if (removed)

View File

@@ -66,7 +66,7 @@ namespace Umbraco.Core.Models.Entities
public int ParentId { get; set; }
/// <inheritdoc />
public void SetParent(ITreeEntity parent) => throw new WontImplementException();
public void SetParent(ITreeEntity parent) => throw new InvalidOperationException("This property won't be implemented.");
/// <inheritdoc />
[DataMember]
@@ -116,7 +116,7 @@ namespace Umbraco.Core.Models.Entities
/// <inheritdoc />
public object DeepClone()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
#endregion
@@ -128,47 +128,47 @@ namespace Umbraco.Core.Models.Entities
public bool IsDirty()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public bool IsPropertyDirty(string propName)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public IEnumerable<string> GetDirtyProperties()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public void ResetDirtyProperties()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public bool WasDirty()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public bool WasPropertyDirty(string propertyName)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public void ResetWereDirtyProperties()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public void ResetDirtyProperties(bool rememberDirty)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
public IEnumerable<string> GetWereDirtyProperties()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
#endregion

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Models.Entities
{
public interface IMemberEntitySlim : IContentEntitySlim
{
}
}

View File

@@ -0,0 +1,13 @@
namespace Umbraco.Core.Models.Entities
{
public class MemberEntitySlim : EntitySlim, IMemberEntitySlim
{
public string ContentTypeAlias { get; set; }
/// <inheritdoc />
public string ContentTypeIcon { get; set; }
/// <inheritdoc />
public string ContentTypeThumbnail { get; set; }
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Models
@@ -43,7 +42,8 @@ namespace Umbraco.Core.Models
public Member(string name, IMemberType contentType)
: base(name, -1, contentType, new PropertyCollection())
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
IsApproved = true;
@@ -63,9 +63,12 @@ namespace Umbraco.Core.Models
public Member(string name, string email, string username, IMemberType contentType, bool isApproved = true)
: base(name, -1, contentType, new PropertyCollection())
{
if (string.IsNullOrWhiteSpace(email)) throw new ArgumentNullOrEmptyException(nameof(email));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullOrEmptyException(nameof(username));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (email == null) throw new ArgumentNullException(nameof(email));
if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(email));
if (username == null) throw new ArgumentNullException(nameof(username));
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(username));
_email = email;
_username = username;

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
@@ -9,7 +8,7 @@ namespace Umbraco.Core.Models
/// </summary>
/// <typeparam name="T"></typeparam>
[DataContract(Name = "pagedCollection", Namespace = "")]
public class PagedResult<T>
public abstract class PagedResult
{
public PagedResult(long totalItems, long pageNumber, long pageSize)
{
@@ -39,9 +38,6 @@ namespace Umbraco.Core.Models
[DataMember(Name = "totalItems")]
public long TotalItems { get; private set; }
[DataMember(Name = "items")]
public IEnumerable<T> Items { get; set; }
/// <summary>
/// Calculates the skip size based on the paged parameters specified
/// </summary>

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a paged result for a model collection
/// </summary>
/// <typeparam name="T"></typeparam>
[DataContract(Name = "pagedCollection", Namespace = "")]
public class PagedResult<T> : PagedResult
{
public PagedResult(long totalItems, long pageNumber, long pageSize)
: base(totalItems, pageNumber, pageSize)
{ }
[DataMember(Name = "items")]
public IEnumerable<T> Items { get; set; }
}
}

View File

@@ -26,8 +26,10 @@ namespace Umbraco.Core.Models
private string _propertyEditorAlias;
private ValueStorageType _valueStorageType;
private bool _mandatory;
private string _mandatoryMessage;
private int _sortOrder;
private string _validationRegExp;
private string _validationRegExpMessage;
private ContentVariation _variations;
/// <summary>
@@ -183,7 +185,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Gets of sets a value indicating whether a value for this property type is required.
/// Gets or sets a value indicating whether a value for this property type is required.
/// </summary>
[DataMember]
public bool Mandatory
@@ -192,6 +194,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _mandatory, nameof(Mandatory));
}
/// <summary>
/// Gets or sets the custom validation message used when a value for this PropertyType is required
/// </summary>
[DataMember]
public string MandatoryMessage
{
get => _mandatoryMessage;
set => SetPropertyValueAndDetectChanges(value, ref _mandatoryMessage, nameof(MandatoryMessage));
}
/// <summary>
/// Gets of sets the sort order of the property type.
/// </summary>
@@ -212,6 +224,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _validationRegExp, nameof(ValidationRegExp));
}
/// <summary>
/// Gets or sets the custom validation message used when a pattern for this PropertyType must be matched
/// </summary>
[DataMember]
public string ValidationRegExpMessage
{
get => _validationRegExpMessage;
set => SetPropertyValueAndDetectChanges(value, ref _validationRegExpMessage, nameof(ValidationRegExpMessage));
}
/// <summary>
/// Gets or sets the content variation of the property type.
/// </summary>

View File

@@ -20,7 +20,9 @@ namespace Umbraco.Core.Models.PublishedContent
{
private ModelType(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias));
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias));
ContentTypeAlias = contentTypeAlias;
Name = "{" + ContentTypeAlias + "}";
}

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
@@ -13,7 +12,8 @@ namespace Umbraco.Core.Models.PublishedContent
/// </summary>
public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
Culture = culture ?? throw new ArgumentNullException(nameof(culture));
Name = name;

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
@@ -19,7 +18,9 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="contentTypeAlias">The content type alias.</param>
public PublishedModelAttribute(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias));
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias));
ContentTypeAlias = contentTypeAlias;
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
@@ -20,7 +19,9 @@ namespace Umbraco.Core.Models
public RelationType(Guid childObjectType, Guid parentObjectType, string alias)
{
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias));
if (alias == null) throw new ArgumentNullException(nameof(alias));
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias));
_childObjectType = childObjectType;
_parentObjectType = parentObjectType;
_alias = alias;
@@ -30,7 +31,9 @@ namespace Umbraco.Core.Models
public RelationType(Guid childObjectType, Guid parentObjectType, string alias, string name)
: this(childObjectType, parentObjectType, alias)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
Name = name;
}

View File

@@ -67,7 +67,7 @@ namespace Umbraco.Core.Models
if (user.Avatar.IsNullOrWhiteSpace())
{
var gravatarHash = user.Email.ToMd5();
var gravatarHash = user.Email.GenerateHash<MD5>();
var gravatarUrl = "https://www.gravatar.com/avatar/" + gravatarHash + "?d=404";
//try Gravatar

View File

@@ -542,7 +542,7 @@ namespace Umbraco.Core
{
return "\"{0}\"".InvariantFormat(obj);
}
if (obj is int || obj is Int16 || obj is Int64 || obj is float || obj is double || obj is bool || obj is int? || obj is Int16? || obj is Int64? || obj is float? || obj is double? || obj is bool?)
if (obj is int || obj is short || obj is long || obj is float || obj is double || obj is bool || obj is int? || obj is float? || obj is double? || obj is bool?)
{
return "{0}".InvariantFormat(obj);
}
@@ -723,7 +723,7 @@ namespace Umbraco.Core
{
return typeConverter;
}
var converter = TypeDescriptor.GetConverter(target);
if (converter.CanConvertFrom(source))
{
@@ -788,6 +788,6 @@ namespace Umbraco.Core
return BoolConvertCache[type] = false;
}
}
}

View File

@@ -787,7 +787,14 @@ namespace Umbraco.Core.Packaging
Mandatory = property.Element("Mandatory") != null
? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true")
: false,
MandatoryMessage = property.Element("MandatoryMessage") != null
? (string)property.Element("MandatoryMessage")
: string.Empty,
ValidationRegExp = (string)property.Element("Validation"),
ValidationRegExpMessage = property.Element("ValidationRegExpMessage") != null
? (string)property.Element("ValidationRegExpMessage")
: string.Empty,
SortOrder = sortOrder,
Variations = property.Element("Variations") != null
? (ContentVariation)Enum.Parse(typeof(ContentVariation), property.Element("Variations").Value)

View File

@@ -470,7 +470,7 @@ namespace Umbraco.Core.Persistence
break;
case SqlDbType.SmallInt:
dataType = typeof(Int16);
dataType = typeof(short);
dataTypeName = "smallint";
break;
@@ -688,34 +688,34 @@ namespace Umbraco.Core.Persistence
DataColumnCollection columns = _schemaTable.Columns;
columns.Add(SchemaTableColumn.ColumnName, typeof(System.String));
columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(System.Int32));
columns.Add(SchemaTableColumn.ColumnSize, typeof(System.Int32));
columns.Add(SchemaTableColumn.NumericPrecision, typeof(System.Int16));
columns.Add(SchemaTableColumn.NumericScale, typeof(System.Int16));
columns.Add(SchemaTableColumn.IsUnique, typeof(System.Boolean));
columns.Add(SchemaTableColumn.IsKey, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(System.String));
columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(System.String));
columns.Add(SchemaTableColumn.BaseColumnName, typeof(System.String));
columns.Add(SchemaTableColumn.BaseSchemaName, typeof(System.String));
columns.Add(SchemaTableColumn.BaseTableName, typeof(System.String));
columns.Add(SchemaTableColumn.DataType, typeof(System.Type));
columns.Add(SchemaTableColumn.AllowDBNull, typeof(System.Boolean));
columns.Add(SchemaTableColumn.ProviderType, typeof(System.Int32));
columns.Add(SchemaTableColumn.IsAliased, typeof(System.Boolean));
columns.Add(SchemaTableColumn.IsExpression, typeof(System.Boolean));
columns.Add(BulkDataReader.IsIdentitySchemaColumn, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(System.Boolean));
columns.Add(SchemaTableColumn.IsLong, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(System.Boolean));
columns.Add(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type));
columns.Add(BulkDataReader.DataTypeNameSchemaColumn, typeof(System.String));
columns.Add(BulkDataReader.XmlSchemaCollectionDatabaseSchemaColumn, typeof(System.String));
columns.Add(BulkDataReader.XmlSchemaCollectionOwningSchemaSchemaColumn, typeof(System.String));
columns.Add(BulkDataReader.XmlSchemaCollectionNameSchemaColumn, typeof(System.String));
columns.Add(SchemaTableColumn.ColumnName, typeof(string));
columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(int));
columns.Add(SchemaTableColumn.ColumnSize, typeof(int));
columns.Add(SchemaTableColumn.NumericPrecision, typeof(short));
columns.Add(SchemaTableColumn.NumericScale, typeof(short));
columns.Add(SchemaTableColumn.IsUnique, typeof(bool));
columns.Add(SchemaTableColumn.IsKey, typeof(bool));
columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(string));
columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(string));
columns.Add(SchemaTableColumn.BaseColumnName, typeof(string));
columns.Add(SchemaTableColumn.BaseSchemaName, typeof(string));
columns.Add(SchemaTableColumn.BaseTableName, typeof(string));
columns.Add(SchemaTableColumn.DataType, typeof(Type));
columns.Add(SchemaTableColumn.AllowDBNull, typeof(bool));
columns.Add(SchemaTableColumn.ProviderType, typeof(int));
columns.Add(SchemaTableColumn.IsAliased, typeof(bool));
columns.Add(SchemaTableColumn.IsExpression, typeof(bool));
columns.Add(BulkDataReader.IsIdentitySchemaColumn, typeof(bool));
columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(bool));
columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(bool));
columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(bool));
columns.Add(SchemaTableColumn.IsLong, typeof(bool));
columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(bool));
columns.Add(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(Type));
columns.Add(BulkDataReader.DataTypeNameSchemaColumn, typeof(string));
columns.Add(BulkDataReader.XmlSchemaCollectionDatabaseSchemaColumn, typeof(string));
columns.Add(BulkDataReader.XmlSchemaCollectionOwningSchemaSchemaColumn, typeof(string));
columns.Add(BulkDataReader.XmlSchemaCollectionNameSchemaColumn, typeof(string));
}
#endregion
@@ -1090,7 +1090,7 @@ namespace Umbraco.Core.Persistence
/// <seealso cref="IDataRecord.GetDecimal(Int32)"/>
public decimal GetDecimal(int i)
{
return (Decimal)GetValue(i);
return (decimal)GetValue(i);
}
/// <summary>

View File

@@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.CompilerServices;
namespace Umbraco.Core.Persistence
{
internal static class DatabaseNodeLockExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ValidateDatabase(IUmbracoDatabase database)
{
if (database == null)
throw new ArgumentNullException("database");
if (database.GetCurrentTransactionIsolationLevel() < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
}
// updating a record within a repeatable-read transaction gets an exclusive lock on
// that record which will be kept until the transaction is ended, effectively locking
// out all other accesses to that record - thus obtaining an exclusive lock over the
// protected resources.
public static void AcquireLockNodeWriteLock(this IUmbracoDatabase database, int nodeId)
{
ValidateDatabase(database);
database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id",
new { @id = nodeId });
}
// reading a record within a repeatable-read transaction gets a shared lock on
// that record which will be kept until the transaction is ended, effectively preventing
// other write accesses to that record - thus obtaining a shared lock over the protected
// resources.
public static void AcquireLockNodeReadLock(this IUmbracoDatabase database, int nodeId)
{
ValidateDatabase(database);
database.ExecuteScalar<int>("SELECT value FROM umbracoLock WHERE id=@id",
new { @id = nodeId });
}
}
}

View File

@@ -43,10 +43,20 @@ namespace Umbraco.Core.Persistence.Dtos
[Constraint(Default = "0")]
public bool Mandatory { get; set; }
[Column("mandatoryMessage")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string MandatoryMessage { get; set; }
[Column("validationRegExp")]
[NullSetting(NullSetting = NullSettings.Null)]
public string ValidationRegExp { get; set; }
[Column("validationRegExpMessage")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string ValidationRegExpMessage { get; set; }
[Column("Description")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(2000)]

View File

@@ -32,9 +32,15 @@ namespace Umbraco.Core.Persistence.Dtos
[Column("mandatory")]
public bool Mandatory { get; set; }
[Column("mandatoryMessage")]
public string MandatoryMessage { get; set; }
[Column("validationRegExp")]
public string ValidationRegExp { get; set; }
[Column("validationRegExpMessage")]
public string ValidationRegExpMessage { get; set; }
[Column("Description")]
public string Description { get; set; }

View File

@@ -1,38 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
namespace Umbraco.Core.Persistence
{
// TODO: Would be good to use this exception type anytime we cannot find an entity
/// <summary>
/// An exception used to indicate that an umbraco entity could not be found
/// An exception used to indicate that an Umbraco entity could not be found.
/// </summary>
/// <seealso cref="System.Exception" />
[Obsolete("Instead of throwing an exception, return null or an HTTP 404 status code instead.")]
[Serializable]
public class EntityNotFoundException : Exception
{
/// <summary>
/// Gets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
/// <remarks>
/// This object should be serializable to prevent a <see cref="SerializationException" /> to be thrown.
/// </remarks>
public object Id { get; private set; }
private readonly string _msg;
public EntityNotFoundException(object id, string msg)
/// <summary>
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
/// </summary>
public EntityNotFoundException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="message">The message.</param>
public EntityNotFoundException(object id, string message)
: base(message)
{
Id = id;
_msg = msg;
}
public EntityNotFoundException(string msg)
/// <summary>
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public EntityNotFoundException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
public EntityNotFoundException(string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
protected EntityNotFoundException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
_msg = msg;
Id = info.GetValue(nameof(Id), typeof(object));
}
public override string Message
/// <summary>
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
/// <exception cref="ArgumentNullException">info</exception>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
get { return _msg; }
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
info.AddValue(nameof(Id), Id);
base.GetObjectData(info, context);
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var result = base.ToString();

View File

@@ -60,8 +60,10 @@ namespace Umbraco.Core.Persistence.Factories
propertyType.Key = typeDto.UniqueId;
propertyType.Name = typeDto.Name;
propertyType.Mandatory = typeDto.Mandatory;
propertyType.MandatoryMessage = typeDto.MandatoryMessage;
propertyType.SortOrder = typeDto.SortOrder;
propertyType.ValidationRegExp = typeDto.ValidationRegExp;
propertyType.ValidationRegExpMessage = typeDto.ValidationRegExpMessage;
propertyType.PropertyGroupId = new Lazy<int>(() => tempGroupDto.Id);
propertyType.CreateDate = createDate;
propertyType.UpdateDate = updateDate;
@@ -124,9 +126,11 @@ namespace Umbraco.Core.Persistence.Factories
DataTypeId = propertyType.DataTypeId,
Description = propertyType.Description,
Mandatory = propertyType.Mandatory,
MandatoryMessage = propertyType.MandatoryMessage,
Name = propertyType.Name,
SortOrder = propertyType.SortOrder,
ValidationRegExp = propertyType.ValidationRegExp,
ValidationRegExpMessage = propertyType.ValidationRegExpMessage,
UniqueId = propertyType.Key,
Variations = (byte)propertyType.Variations
};

View File

@@ -4,59 +4,51 @@ using System.Runtime.Serialization;
namespace Umbraco.Core.Persistence.FaultHandling
{
/// <summary>
/// The special type of exception that provides managed exit from a retry loop. The user code can use this
/// exception to notify the retry policy that no further retry attempts are required.
/// The special type of exception that provides managed exit from a retry loop. The user code can use this exception to notify the retry policy that no further retry attempts are required.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public sealed class RetryLimitExceededException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class with a default error message.
/// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class with a default error message.
/// </summary>
public RetryLimitExceededException()
: this("RetryLimitExceeded")
{
}
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class with a specified error message.
/// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public RetryLimitExceededException(string message)
: base(message)
{
}
{ }
/// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class with a reference to the inner exception
/// that is the cause of this exception.
/// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class with a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="innerException">The exception that is the cause of the current exception.</param>
public RetryLimitExceededException(Exception innerException)
: base(innerException != null ? innerException.Message : "RetryLimitExceeded", innerException)
{
}
: base(null, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class.
/// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception.</param>
public RetryLimitExceededException(string message, Exception innerException)
: base(message, innerException)
{
}
{ }
/// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class.
/// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null.</exception>
/// <exception cref="T:System.Runtime.Serialization.SerializationException">The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
private RetryLimitExceededException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
{ }
}
}

View File

@@ -24,9 +24,11 @@ namespace Umbraco.Core.Persistence.Mappers
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.DataTypeId), nameof(PropertyTypeDto.DataTypeId));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Mandatory), nameof(PropertyTypeDto.Mandatory));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.MandatoryMessage), nameof(PropertyTypeDto.MandatoryMessage));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Name), nameof(PropertyTypeDto.Name));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExp), nameof(PropertyTypeDto.ValidationRegExp));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExpMessage), nameof(PropertyTypeDto.ValidationRegExpMessage));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.PropertyEditorAlias), nameof(DataTypeDto.EditorAlias));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType));
}

View File

@@ -7,5 +7,12 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IDataTypeRepository : IReadWriteQueryRepository<int, IDataType>
{
IEnumerable<MoveEventInfo<IDataType>> Move(IDataType toMove, EntityContainer container);
/// <summary>
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id);
}
}

View File

@@ -297,10 +297,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Id = dto.Id,
Key = dto.UniqueId,
Mandatory = dto.Mandatory,
MandatoryMessage = dto.MandatoryMessage,
Name = dto.Name,
PropertyGroupId = groupId.HasValue ? new Lazy<int>(() => groupId.Value) : null,
SortOrder = dto.SortOrder,
ValidationRegExp = dto.ValidationRegExp,
ValidationRegExpMessage = dto.ValidationRegExpMessage,
Variations = (ContentVariation)dto.Variations
};
}

View File

@@ -1,4 +1,5 @@
using System;

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
@@ -218,6 +219,7 @@ AND umbracoNode.nodeObjectType = @objectType",
protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
{
CorrectPropertyTypeVariations(entity);
ValidateVariations(entity);
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
@@ -410,26 +412,7 @@ AND umbracoNode.id <> @id",
// note: this only deals with *local* property types, we're dealing w/compositions later below
foreach (var propertyType in entity.PropertyTypes)
{
if (contentTypeVariationChanging)
{
// content type is changing
switch (newContentTypeVariation)
{
case ContentVariation.Nothing: // changing to Nothing
// all property types must change to Nothing
propertyType.Variations = ContentVariation.Nothing;
break;
case ContentVariation.Culture: // changing to Culture
// all property types can remain Nothing
break;
case ContentVariation.CultureAndSegment:
case ContentVariation.Segment:
default:
throw new NotSupportedException(); // TODO: Support this
}
}
// then, track each property individually
// track each property individually
if (propertyType.IsPropertyDirty("Variations"))
{
// allocate the list only when needed
@@ -455,23 +438,19 @@ AND umbracoNode.id <> @id",
// via composition, with their original variations (ie not filtered by this
// content type variations - we need this true value to make decisions.
foreach (var propertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes)
propertyTypeVariationChanges = propertyTypeVariationChanges ?? new Dictionary<int, (ContentVariation, ContentVariation)>();
foreach (var composedPropertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes)
{
if (propertyType.VariesBySegment() || newContentTypeVariation.VariesBySegment())
throw new NotSupportedException(); // TODO: support this
if (composedPropertyType.Variations == ContentVariation.Nothing) continue;
if (propertyType.Variations == ContentVariation.Culture)
{
if (propertyTypeVariationChanges == null)
propertyTypeVariationChanges = new Dictionary<int, (ContentVariation, ContentVariation)>();
// Determine target variation of the composed property type.
// The composed property is only considered culture variant when the base content type is also culture variant.
// The composed property is only considered segment variant when the base content type is also segment variant.
// Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture
var target = newContentTypeVariation & composedPropertyType.Variations;
// if content type moves to Culture, property type becomes Culture here again
// if content type moves to Nothing, property type becomes Nothing here
if (newContentTypeVariation == ContentVariation.Culture)
propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Nothing, ContentVariation.Culture);
else if (newContentTypeVariation == ContentVariation.Nothing)
propertyTypeVariationChanges[propertyType.Id] = (ContentVariation.Culture, ContentVariation.Nothing);
}
propertyTypeVariationChanges[composedPropertyType.Id] = (composedPropertyType.Variations, target);
}
}
@@ -512,7 +491,7 @@ AND umbracoNode.id <> @id",
var impacted = GetImpactedContentTypes(entity, all);
// if some property types have actually changed, move their variant data
if (propertyTypeVariationChanges != null)
if (propertyTypeVariationChanges?.Count > 0)
MovePropertyTypeVariantData(propertyTypeVariationChanges, impacted);
// deal with orphan properties: those that were in a deleted tab,
@@ -524,23 +503,40 @@ AND umbracoNode.id <> @id",
CommonRepository.ClearCache(); // always
}
/// <summary>
/// Corrects the property type variations for the given entity
/// to make sure the property type variation is compatible with the
/// variation set on the entity itself.
/// </summary>
/// <param name="entity">Entity to correct properties for</param>
private void CorrectPropertyTypeVariations(IContentTypeComposition entity)
{
// Update property variations based on the content type variation
foreach (var propertyType in entity.PropertyTypes)
{
// Determine variation for the property type.
// The property is only considered culture variant when the base content type is also culture variant.
// The property is only considered segment variant when the base content type is also segment variant.
// Example: Culture variant content type with a Culture+Segment variant property type will become ContentVariation.Culture
propertyType.Variations = entity.Variations & propertyType.Variations;
}
}
/// <summary>
/// Ensures that no property types are flagged for a variance that is not supported by the content type itself
/// </summary>
/// <param name="entity"></param>
/// <param name="entity">The entity for which the property types will be validated</param>
private void ValidateVariations(IContentTypeComposition entity)
{
//if the entity does not vary at all, then the property cannot have a variance value greater than it
if (entity.Variations == ContentVariation.Nothing)
foreach (var prop in entity.PropertyTypes)
{
foreach (var prop in entity.PropertyTypes)
{
if (prop.IsPropertyDirty(nameof(prop.Variations)) && prop.Variations > entity.Variations)
throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}");
}
// The variation of a property is only allowed if all its variation flags
// are also set on the entity itself. It cannot set anything that is not also set by the content type.
// For example, when entity.Variations is set to Culture a property cannot be set to Segment.
var isValid = entity.Variations.HasFlag(prop.Variations);
if (!isValid)
throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}");
}
}
private IEnumerable<IContentTypeComposition> GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable<IContentTypeComposition> all)
@@ -661,27 +657,27 @@ AND umbracoNode.id <> @id",
var impactedL = impacted.Select(x => x.Id).ToList();
//Group by the "To" variation so we can bulk update in the correct batches
foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value))
{
var propertyTypeIds = grouping.Select(x => x.Key).ToList();
var toVariation = grouping.Key;
var (FromVariation, ToVariation) = grouping.Key;
switch (toVariation)
var fromCultureEnabled = FromVariation.HasFlag(ContentVariation.Culture);
var toCultureEnabled = ToVariation.HasFlag(ContentVariation.Culture);
if (!fromCultureEnabled && toCultureEnabled)
{
case ContentVariation.Culture:
CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL);
CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL);
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
break;
case ContentVariation.Nothing:
CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL);
CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL);
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
break;
case ContentVariation.CultureAndSegment:
case ContentVariation.Segment:
default:
throw new NotSupportedException(); // TODO: Support this
// Culture has been enabled
CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL);
CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL);
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
}
else if (fromCultureEnabled && !toCultureEnabled)
{
// Culture has been disabled
CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL);
CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL);
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
}
}
}
@@ -693,78 +689,72 @@ AND umbracoNode.id <> @id",
{
var defaultLanguageId = GetDefaultLanguageId();
switch (toVariation)
var cultureIsNotEnabled = !fromVariation.HasFlag(ContentVariation.Culture);
var cultureWillBeEnabled = toVariation.HasFlag(ContentVariation.Culture);
if (cultureIsNotEnabled && cultureWillBeEnabled)
{
case ContentVariation.Culture:
//move the names
//first clear out any existing names that might already exists under the default lang
//there's 2x tables to update
//move the names
//first clear out any existing names that might already exists under the default lang
//there's 2x tables to update
//clear out the versionCultureVariation table
var sqlSelect = Sql().Select<ContentVersionCultureVariationDto>(x => x.Id)
.From<ContentVersionCultureVariationDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>(x => x.Id, x => x.VersionId)
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<ContentVersionCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
var sqlDelete = Sql()
.Delete<ContentVersionCultureVariationDto>()
.WhereIn<ContentVersionCultureVariationDto>(x => x.Id, sqlSelect);
//clear out the versionCultureVariation table
var sqlSelect = Sql().Select<ContentVersionCultureVariationDto>(x => x.Id)
.From<ContentVersionCultureVariationDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, ContentVersionCultureVariationDto>(x => x.Id, x => x.VersionId)
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<ContentVersionCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
var sqlDelete = Sql()
.Delete<ContentVersionCultureVariationDto>()
.WhereIn<ContentVersionCultureVariationDto>(x => x.Id, sqlSelect);
Database.Execute(sqlDelete);
Database.Execute(sqlDelete);
//clear out the documentCultureVariation table
sqlSelect = Sql().Select<DocumentCultureVariationDto>(x => x.Id)
.From<DocumentCultureVariationDto>()
.InnerJoin<ContentDto>().On<ContentDto, DocumentCultureVariationDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<DocumentCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
sqlDelete = Sql()
.Delete<DocumentCultureVariationDto>()
.WhereIn<DocumentCultureVariationDto>(x => x.Id, sqlSelect);
//clear out the documentCultureVariation table
sqlSelect = Sql().Select<DocumentCultureVariationDto>(x => x.Id)
.From<DocumentCultureVariationDto>()
.InnerJoin<ContentDto>().On<ContentDto, DocumentCultureVariationDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id)
.Where<DocumentCultureVariationDto>(x => x.LanguageId == defaultLanguageId);
sqlDelete = Sql()
.Delete<DocumentCultureVariationDto>()
.WhereIn<DocumentCultureVariationDto>(x => x.Id, sqlSelect);
Database.Execute(sqlDelete);
Database.Execute(sqlDelete);
//now we need to insert names into these 2 tables based on the invariant data
//now we need to insert names into these 2 tables based on the invariant data
//insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang
var cols = Sql().Columns<ContentVersionCultureVariationDto>(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId);
sqlSelect = Sql().Select<ContentVersionDto>(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate)
.Append($", {defaultLanguageId}") //default language ID
.From<ContentVersionDto>()
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
//insert rows into the versionCultureVariationDto table based on the data from contentVersionDto for the default lang
var cols = Sql().Columns<ContentVersionCultureVariationDto>(x => x.VersionId, x => x.Name, x => x.UpdateUserId, x => x.UpdateDate, x => x.LanguageId);
sqlSelect = Sql().Select<ContentVersionDto>(x => x.Id, x => x.Text, x => x.UserId, x => x.VersionDate)
.Append($", {defaultLanguageId}") //default language ID
.From<ContentVersionDto>()
.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
var sqlInsert = Sql($"INSERT INTO {ContentVersionCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
Database.Execute(sqlInsert);
Database.Execute(sqlInsert);
//insert rows into the documentCultureVariation table
cols = Sql().Columns<DocumentCultureVariationDto>(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId);
sqlSelect = Sql().Select<DocumentDto>(x => x.NodeId, x => x.Edited, x => x.Published)
.AndSelect<NodeDto>(x => x.Text)
.Append($", 1, {defaultLanguageId}") //make Available + default language ID
.From<DocumentDto>()
.InnerJoin<NodeDto>().On<NodeDto, DocumentDto>(x => x.NodeId, x => x.NodeId)
.InnerJoin<ContentDto>().On<ContentDto, NodeDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
//insert rows into the documentCultureVariation table
cols = Sql().Columns<DocumentCultureVariationDto>(x => x.NodeId, x => x.Edited, x => x.Published, x => x.Name, x => x.Available, x => x.LanguageId);
sqlSelect = Sql().Select<DocumentDto>(x => x.NodeId, x => x.Edited, x => x.Published)
.AndSelect<NodeDto>(x => x.Text)
.Append($", 1, {defaultLanguageId}") //make Available + default language ID
.From<DocumentDto>()
.InnerJoin<NodeDto>().On<NodeDto, DocumentDto>(x => x.NodeId, x => x.NodeId)
.InnerJoin<ContentDto>().On<ContentDto, NodeDto>(x => x.NodeId, x => x.NodeId)
.Where<ContentDto>(x => x.ContentTypeId == contentType.Id);
sqlInsert = Sql($"INSERT INTO {DocumentCultureVariationDto.TableName} ({cols})").Append(sqlSelect);
Database.Execute(sqlInsert);
}
else
{
//we don't need to move the names! this is because we always keep the invariant names with the name of the default language.
Database.Execute(sqlInsert);
break;
case ContentVariation.Nothing:
//we don't need to move the names! this is because we always keep the invariant names with the name of the default language.
//however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :(
// if we want these SQL statements back, look into GIT history
break;
case ContentVariation.CultureAndSegment:
case ContentVariation.Segment:
default:
throw new NotSupportedException(); // TODO: Support this
//however, if we were to move names, we could do this: BUT this doesn't work with SQLCE, for that we'd have to update row by row :(
// if we want these SQL statements back, look into GIT history
}
}

View File

@@ -279,6 +279,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return moveInfo;
}
public IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id)
{
if (id == default)
return new Dictionary<Udi, IEnumerable<string>>();
var sql = Sql()
.Select<ContentTypeDto>(ct => ct.Select(node => node.NodeDto))
.AndSelect<PropertyTypeDto>(pt => Alias(pt.Alias, "ptAlias"), pt => Alias(pt.Name, "ptName"))
.From<PropertyTypeDto>()
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, PropertyTypeDto>(ct => ct.NodeId, pt => pt.ContentTypeId)
.InnerJoin<NodeDto>().On<NodeDto, ContentTypeDto>(n => n.NodeId, ct => ct.NodeId)
.Where<PropertyTypeDto>(pt => pt.DataTypeId == id)
.OrderBy<NodeDto>(node => node.NodeId)
.AndBy<PropertyTypeDto>(pt => pt.Alias);
var dtos = Database.FetchOneToMany<ContentTypeReferenceDto>(ct => ct.PropertyTypes, sql);
return dtos.ToDictionary(
x => (Udi)new GuidUdi(ObjectTypes.GetUdiType(x.NodeDto.NodeObjectType.Value), x.NodeDto.UniqueId).EnsureClosed(),
x => (IEnumerable<string>)x.PropertyTypes.Select(p => p.Alias).ToList());
}
private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql
@@ -291,5 +313,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return SimilarNodeName.GetUniqueName(names, id, nodeName);
}
[TableName(Constants.DatabaseSchema.Tables.ContentType)]
private class ContentTypeReferenceDto : ContentTypeDto
{
[ResultColumn]
[Reference(ReferenceType.Many)]
public List<PropertyTypeReferenceDto> PropertyTypes { get; set; }
}
[TableName(Constants.DatabaseSchema.Tables.PropertyType)]
private class PropertyTypeReferenceDto
{
[Column("ptAlias")]
public string Alias { get; set; }
[Column("ptName")]
public string Name { get; set; }
}
}
}

View File

@@ -248,14 +248,63 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return dto == null ? null : MapDtoToContent(dto);
}
// deletes a specific version
public override void DeleteVersion(int versionId)
{
// TODO: test object node type?
// get the version we want to delete
var template = SqlContext.Templates.Get("Umbraco.Core.DocumentRepository.GetVersion", tsql =>
tsql.Select<ContentVersionDto>()
.AndSelect<DocumentVersionDto>()
.From<ContentVersionDto>()
.InnerJoin<DocumentVersionDto>()
.On<ContentVersionDto, DocumentVersionDto>((c, d) => c.Id == d.Id)
.Where<ContentVersionDto>(x => x.Id == SqlTemplate.Arg<int>("versionId"))
);
var versionDto = Database.Fetch<DocumentVersionDto>(template.Sql(new { versionId })).FirstOrDefault();
// nothing to delete
if (versionDto == null)
return;
// don't delete the current or published version
if (versionDto.ContentVersionDto.Current)
throw new InvalidOperationException("Cannot delete the current version.");
else if (versionDto.Published)
throw new InvalidOperationException("Cannot delete the published version.");
PerformDeleteVersion(versionDto.ContentVersionDto.NodeId, versionId);
}
// deletes all versions of an entity, older than a date.
public override void DeleteVersions(int nodeId, DateTime versionDate)
{
// TODO: test object node type?
// get the versions we want to delete, excluding the current one
var template = SqlContext.Templates.Get("Umbraco.Core.DocumentRepository.GetVersions", tsql =>
tsql.Select<ContentVersionDto>()
.From<ContentVersionDto>()
.InnerJoin<DocumentVersionDto>()
.On<ContentVersionDto, DocumentVersionDto>((c, d) => c.Id == d.Id)
.Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.Arg<int>("nodeId") && !x.Current && x.VersionDate < SqlTemplate.Arg<DateTime>("versionDate"))
.Where<DocumentVersionDto>( x => !x.Published)
);
var versionDtos = Database.Fetch<ContentVersionDto>(template.Sql(new { nodeId, versionDate }));
foreach (var versionDto in versionDtos)
PerformDeleteVersion(versionDto.NodeId, versionDto.Id);
}
protected override void PerformDeleteVersion(int id, int versionId)
{
// raise event first else potential FK issues
OnUowRemovingVersion(new ScopedVersionEventArgs(AmbientScope, id, versionId));
Database.Delete<PropertyDataDto>("WHERE versionId = @versionId", new { versionId });
Database.Delete<ContentVersionDto>("WHERE id = @versionId", new { versionId });
Database.Delete<ContentVersionCultureVariationDto>("WHERE versionId = @versionId", new { versionId });
Database.Delete<DocumentVersionDto>("WHERE id = @versionId", new { versionId });
Database.Delete<ContentVersionDto>("WHERE id = @versionId", new { versionId });
}
#endregion
@@ -875,32 +924,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<string> GetDeleteClauses()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override void PersistNewItem(IContent entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override void PersistUpdatedItem(IContent entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override string GetBaseWhereClause()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -129,6 +128,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override void PersistDeletedItem(EntityContainer entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(entity);
var nodeDto = Database.FirstOrDefault<NodeDto>(Sql().SelectAll()
@@ -162,9 +162,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override void PersistNewItem(EntityContainer entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(entity);
if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentNullOrEmptyException("entity.Name");
if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null.");
if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters.");
entity.Name = entity.Name.Trim();
// guard against duplicates
@@ -184,7 +186,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.Where<NodeDto>(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parentDto == null)
throw new NullReferenceException("Could not find parent container with id " + entity.ParentId);
throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId);
level = parentDto.Level;
path = parentDto.Path;
@@ -223,10 +225,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
//
protected override void PersistUpdatedItem(EntityContainer entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(entity);
if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null.");
if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters.");
entity.Name = entity.Name.Trim();
if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentNullOrEmptyException("entity.Name");
// find container to update
var nodeDto = Database.FirstOrDefault<NodeDto>(Sql().SelectAll()
@@ -255,7 +259,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.Where<NodeDto>(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parent == null)
throw new NullReferenceException("Could not find parent container with id " + entity.ParentId);
throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId);
nodeDto.Level = Convert.ToInt16(parent.Level + 1);
nodeDto.Path = parent.Path + "," + nodeDto.NodeId;

View File

@@ -42,8 +42,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetBaseWhere(isContent, isMedia, false, x =>
var sql = GetBaseWhere(isContent, isMedia, isMember, false, x =>
{
if (filter == null) return;
foreach (var filterClause in filter.GetWhereClauses())
@@ -54,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var translator = new SqlTranslator<IUmbracoEntity>(sql, query);
sql = translator.Translate();
sql = AddGroupBy(isContent, isMedia, sql, ordering.IsEmpty);
sql = AddGroupBy(isContent, isMedia, isMember, sql, ordering.IsEmpty);
if (!ordering.IsEmpty)
{
@@ -81,6 +82,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
dtos = page.Items;
totalRecords = page.TotalItems;
}
else if (isMember)
{
var page = Database.Page<MemberEntityDto>(pageIndexToFetch, pageSize, sql);
dtos = page.Items;
totalRecords = page.TotalItems;
}
else
{
var page = Database.Page<BaseDto>(pageIndexToFetch, pageSize, sql);
@@ -88,7 +95,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
totalRecords = page.TotalItems;
}
var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray();
var entities = dtos.Select(x => BuildEntity(isContent, isMedia, isMember, x)).ToArray();
if (isContent)
BuildVariants(entities.Cast<DocumentEntitySlim>());
@@ -98,13 +105,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public IEntitySlim Get(Guid key)
{
var sql = GetBaseWhere(false, false, false, key);
var sql = GetBaseWhere(false, false, false, false, key);
var dto = Database.FirstOrDefault<BaseDto>(sql);
return dto == null ? null : BuildEntity(false, false, dto);
return dto == null ? null : BuildEntity(false, false, false, dto);
}
private IEntitySlim GetEntity(Sql<ISqlContext> sql, bool isContent, bool isMedia)
private IEntitySlim GetEntity(Sql<ISqlContext> sql, bool isContent, bool isMedia, bool isMember)
{
//isContent is going to return a 1:M result now with the variants so we need to do different things
if (isContent)
@@ -120,7 +127,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (dto == null) return null;
var entity = BuildEntity(false, isMedia, dto);
var entity = BuildEntity(false, isMedia, isMember, dto);
return entity;
}
@@ -129,25 +136,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectTypeId == Constants.ObjectTypes.Media;
var isMember = objectTypeId == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, key);
return GetEntity(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectTypeId, key);
return GetEntity(sql, isContent, isMedia, isMember);
}
public IEntitySlim Get(int id)
{
var sql = GetBaseWhere(false, false, false, id);
var sql = GetBaseWhere(false, false, false, false, id);
var dto = Database.FirstOrDefault<BaseDto>(sql);
return dto == null ? null : BuildEntity(false, false, dto);
return dto == null ? null : BuildEntity(false, false, false, dto);
}
public IEntitySlim Get(int id, Guid objectTypeId)
{
var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectTypeId == Constants.ObjectTypes.Media;
var isMember = objectTypeId == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, id);
return GetEntity(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectTypeId, id);
return GetEntity(sql, isContent, isMedia, isMember);
}
public IEnumerable<IEntitySlim> GetAll(Guid objectType, params int[] ids)
@@ -164,7 +173,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
: PerformGetAll(objectType);
}
private IEnumerable<IEntitySlim> GetEntities(Sql<ISqlContext> sql, bool isContent, bool isMedia)
private IEnumerable<IEntitySlim> GetEntities(Sql<ISqlContext> sql, bool isContent, bool isMedia, bool isMember)
{
//isContent is going to return a 1:M result now with the variants so we need to do different things
if (isContent)
@@ -180,7 +189,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
? (IEnumerable<BaseDto>)Database.Fetch<MediaEntityDto>(sql)
: Database.Fetch<BaseDto>(sql);
var entities = dtos.Select(x => BuildEntity(false, isMedia, x)).ToArray();
var entities = dtos.Select(x => BuildEntity(false, isMedia, isMember, x)).ToArray();
return entities;
}
@@ -189,9 +198,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectType, filter);
return GetEntities(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectType, filter);
return GetEntities(sql, isContent, isMedia, isMember);
}
public IEnumerable<TreeEntityPath> GetAllPaths(Guid objectType, params int[] ids)
@@ -218,26 +228,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query)
{
var sqlClause = GetBase(false, false, null);
var sqlClause = GetBase(false, false, false, null);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate();
sql = AddGroupBy(false, false, sql, true);
sql = AddGroupBy(false, false, false, sql, true);
var dtos = Database.Fetch<BaseDto>(sql);
return dtos.Select(x => BuildEntity(false, false, x)).ToList();
return dtos.Select(x => BuildEntity(false, false, false, x)).ToList();
}
public IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectType)
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetBaseWhere(isContent, isMedia, false, null, objectType);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, null, objectType);
var translator = new SqlTranslator<IUmbracoEntity>(sql, query);
sql = translator.Translate();
sql = AddGroupBy(isContent, isMedia, sql, true);
sql = AddGroupBy(isContent, isMedia, isMember, sql, true);
return GetEntities(sql, isContent, isMedia);
return GetEntities(sql, isContent, isMedia, isMember);
}
public UmbracoObjectTypes GetObjectType(int id)
@@ -329,29 +340,29 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
// gets the full sql for a given object type and a given unique id
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, Guid uniqueId)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, Guid uniqueId)
{
var sql = GetBaseWhere(isContent, isMedia, false, objectType, uniqueId);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, objectType, uniqueId);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the full sql for a given object type and a given node id
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, int nodeId)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, int nodeId)
{
var sql = GetBaseWhere(isContent, isMedia, false, objectType, nodeId);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, objectType, nodeId);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the full sql for a given object type, with a given filter
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, Action<Sql<ISqlContext>> filter)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, Action<Sql<ISqlContext>> filter)
{
var sql = GetBaseWhere(isContent, isMedia, false, filter, objectType);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, filter, objectType);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM [+ filter] sql
// always from the 'current' content version
protected Sql<ISqlContext> GetBase(bool isContent, bool isMedia, Action<Sql<ISqlContext>> filter, bool isCount = false)
protected Sql<ISqlContext> GetBase(bool isContent, bool isMedia, bool isMember, Action<Sql<ISqlContext>> filter, bool isCount = false)
{
var sql = Sql();
@@ -366,7 +377,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.AndSelect<NodeDto>(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate)
.Append(", COUNT(child.id) AS children");
if (isContent || isMedia)
if (isContent || isMedia || isMember)
sql
.AndSelect<ContentVersionDto>(x => Alias(x.Id, "versionId"))
.AndSelect<ContentTypeDto>(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations);
@@ -387,7 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql
.From<NodeDto>();
if (isContent || isMedia)
if (isContent || isMedia || isMember)
{
sql
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
@@ -422,49 +433,49 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// gets the base SELECT + FROM [+ filter] + WHERE sql
// for a given object type, with a given filter
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Action<Sql<ISqlContext>> filter, Guid objectType)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Action<Sql<ISqlContext>> filter, Guid objectType)
{
return GetBase(isContent, isMedia, filter, isCount)
return GetBase(isContent, isMedia, isMember, filter, isCount)
.Where<NodeDto>(x => x.NodeObjectType == objectType);
}
// gets the base SELECT + FROM + WHERE sql
// for a given node id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, int id)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, int id)
{
var sql = GetBase(isContent, isMedia, null, isCount)
var sql = GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.NodeId == id);
return AddGroupBy(isContent, isMedia, sql, true);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM + WHERE sql
// for a given unique id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid uniqueId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid uniqueId)
{
var sql = GetBase(isContent, isMedia, null, isCount)
var sql = GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.UniqueId == uniqueId);
return AddGroupBy(isContent, isMedia, sql, true);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and node id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid objectType, int nodeId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid objectType, int nodeId)
{
return GetBase(isContent, isMedia, null, isCount)
return GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.NodeId == nodeId && x.NodeObjectType == objectType);
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and unique id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid objectType, Guid uniqueId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid objectType, Guid uniqueId)
{
return GetBase(isContent, isMedia, null, isCount)
return GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.UniqueId == uniqueId && x.NodeObjectType == objectType);
}
// gets the GROUP BY / ORDER BY sql
// required in order to count children
protected Sql<ISqlContext> AddGroupBy(bool isContent, bool isMedia, Sql<ISqlContext> sql, bool defaultSort)
protected Sql<ISqlContext> AddGroupBy(bool isContent, bool isMedia, bool isMember, Sql<ISqlContext> sql, bool defaultSort)
{
sql
.GroupBy<NodeDto>(x => x.NodeId, x => x.Trashed, x => x.ParentId, x => x.UserId, x => x.Level, x => x.Path)
@@ -483,7 +494,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
if (isContent || isMedia)
if (isContent || isMedia || isMember)
sql
.AndBy<ContentVersionDto>(x => x.Id)
.AndBy<ContentTypeDto>(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations);
@@ -528,6 +539,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public string MediaPath { get; set; }
}
private class MemberEntityDto : BaseDto
{
}
public class VariantInfoDto
{
public int NodeId { get; set; }
@@ -574,12 +589,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
#region Factory
private EntitySlim BuildEntity(bool isContent, bool isMedia, BaseDto dto)
private EntitySlim BuildEntity(bool isContent, bool isMedia, bool isMember, BaseDto dto)
{
if (isContent)
return BuildDocumentEntity(dto);
if (isMedia)
return BuildMediaEntity(dto);
if (isMember)
return BuildMemberEntity(dto);
// EntitySlim does not track changes
var entity = new EntitySlim();
@@ -644,6 +661,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return entity;
}
private MemberEntitySlim BuildMemberEntity(BaseDto dto)
{
// EntitySlim does not track changes
var entity = new MemberEntitySlim();
BuildEntity(entity, dto);
entity.ContentTypeAlias = dto.Alias;
entity.ContentTypeIcon = dto.Icon;
entity.ContentTypeThumbnail = dto.Thumbnail;
return entity;
}
#endregion
}
}

View File

@@ -182,6 +182,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
throw new InvalidOperationException($"Cannot save the default language ({entity.IsoCode}) as non-default. Make another language the default language instead.");
}
if (entity.IsPropertyDirty(nameof(ILanguage.IsoCode)))
{
//if the iso code is changing, ensure there's not another lang with the same code already assigned
var sameCode = Sql()
.SelectCount()
.From<LanguageDto>()
.Where<LanguageDto>(x => x.IsoCode == entity.IsoCode && x.Id != entity.Id);
var countOfSameCode = Database.ExecuteScalar<int>(sameCode);
if (countOfSameCode > 0)
throw new InvalidOperationException($"Cannot update the language to a new culture: {entity.IsoCode} since that culture is already assigned to another language entity.");
}
// fallback cycles are detected at service level
// update

View File

@@ -425,32 +425,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<string> GetDeleteClauses()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override void PersistNewItem(IMedia entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override void PersistUpdatedItem(IMedia entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override string GetBaseWhereClause()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
}

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// joining the type so we can do a query against the member type - not sure if this adds much overhead or not?
// the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content
// types by default on the document and media repo's so we can query by content type there too.
// types by default on the document and media repos so we can query by content type there too.
.InnerJoin<ContentTypeDto>().On<ContentDto, ContentTypeDto>(left => left.ContentTypeId, right => right.NodeId);
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
@@ -546,6 +546,15 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (ordering.OrderBy.InvariantEquals("userName"))
return SqlSyntax.GetFieldName<MemberDto>(x => x.LoginName);
if (ordering.OrderBy.InvariantEquals("updateDate"))
return SqlSyntax.GetFieldName<ContentVersionDto>(x => x.VersionDate);
if (ordering.OrderBy.InvariantEquals("createDate"))
return SqlSyntax.GetFieldName<NodeDto>(x => x.CreateDate);
if (ordering.OrderBy.InvariantEquals("contentTypeAlias"))
return SqlSyntax.GetFieldName<ContentTypeDto>(x => x.Alias);
return base.ApplySystemOrdering(ref sql, ordering);
}

View File

@@ -225,8 +225,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (builtinProperties.ContainsKey(propertyType.Alias))
{
//this reset's its current data type reference which will be re-assigned based on the property editor assigned on the next line
propertyType.DataTypeId = 0;
propertyType.DataTypeKey = default;
var propDefinition = builtinProperties[propertyType.Alias];
if (propDefinition != null)
{
propertyType.DataTypeId = propDefinition.DataTypeId;
propertyType.DataTypeKey = propDefinition.DataTypeKey;
}
else
{
propertyType.DataTypeId = 0;
propertyType.DataTypeKey = default;
}
}
}
}

View File

@@ -252,27 +252,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override ContentPermissionSet PerformGet(int id)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<ContentPermissionSet> PerformGetAll(params int[] ids)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<ContentPermissionSet> PerformGetByQuery(IQuery<ContentPermissionSet> query)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override string GetBaseWhereClause()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<string> GetDeleteClauses()
@@ -280,11 +280,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return new List<string>();
}
protected override Guid NodeObjectTypeId => throw new WontImplementException();
protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented.");
protected override void PersistDeletedItem(ContentPermissionSet entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
#endregion

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
@@ -105,7 +106,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
CreateDateUtc = redirectUrl.CreateDateUtc,
Url = redirectUrl.Url,
Culture = redirectUrl.Culture,
UrlHash = redirectUrl.Url.ToSHA1()
UrlHash = redirectUrl.Url.GenerateHash<SHA1>()
};
}
@@ -134,7 +135,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
public IRedirectUrl Get(string url, Guid contentKey, string culture)
{
var urlHash = url.ToSHA1();
var urlHash = url.GenerateHash<SHA1>();
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash && x.ContentKey == contentKey && x.Culture == culture);
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
return dto == null ? null : Map(dto);
@@ -157,7 +158,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
public IRedirectUrl GetMostRecentUrl(string url)
{
var urlHash = url.ToSHA1();
var urlHash = url.GenerateHash<SHA1>();
var sql = GetBaseQuery(false)
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash)
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc);

View File

@@ -75,19 +75,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected sealed override IEnumerable<string> GetDeleteClauses()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected sealed override Guid NodeObjectTypeId => throw new WontImplementException();
protected sealed override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented.");
protected sealed override void PersistNewItem(TEntity entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected sealed override void PersistUpdatedItem(TEntity entity)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
#endregion

View File

@@ -286,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return list;
}
protected override Guid NodeObjectTypeId => throw new WontImplementException();
protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented.");
protected override void PersistNewItem(IUserGroup entity)
{
@@ -370,35 +370,35 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override UserGroupWithUsers PerformGet(int id)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<UserGroupWithUsers> PerformGetAll(params int[] ids)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<UserGroupWithUsers> PerformGetByQuery(IQuery<UserGroupWithUsers> query)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override string GetBaseWhereClause()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override IEnumerable<string> GetDeleteClauses()
{
throw new WontImplementException();
throw new InvalidOperationException("This method won't be implemented.");
}
protected override Guid NodeObjectTypeId => throw new WontImplementException();
protected override Guid NodeObjectTypeId => throw new InvalidOperationException("This property won't be implemented.");
#endregion

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text.RegularExpressions;
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
@@ -76,6 +77,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
string ConvertIntegerToOrderableString { get; }
string ConvertDateToOrderableString { get; }
string ConvertDecimalToOrderableString { get; }
/// <summary>
/// Returns the default isolation level for the database
/// </summary>
IsolationLevel DefaultIsolationLevel { get; }
IEnumerable<string> GetTablesInSchema(IDatabase db);
IEnumerable<ColumnInfo> GetColumnsInSchema(IDatabase db);
@@ -121,5 +127,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// unspecified.</para>
/// </remarks>
bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName);
void ReadLock(IDatabase db, params int[] lockIds);
void WriteLock(IDatabase db, params int[] lockIds);
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlServerCe;
using System.Linq;
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
@@ -52,6 +54,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return "(" + string.Join("+", args) + ")";
}
public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.RepeatableRead;
public override string FormatColumnRename(string tableName, string oldName, string newName)
{
//NOTE Sql CE doesn't support renaming a column, so a new column needs to be created, then copy data and finally remove old column
@@ -152,6 +156,39 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault()
return result > 0;
}
public override void WriteLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
db.Execute(@"SET LOCK_TIMEOUT 1800;");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.Execute(@"UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
if (i == 0) // ensure we are actually locking!
throw new ArgumentException($"LockObject with id={lockId} does not exist.");
}
}
public override void ReadLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new ArgumentException($"LockObject with id={lockId} does not exist.");
}
}
protected override string FormatIdentity(ColumnDefinition column)
{
return column.IsIdentity ? GetIdentityString(column) : string.Empty;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using NPoco;
using Umbraco.Core.Logging;
@@ -179,6 +180,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return items.Select(x => x.TABLE_NAME).Cast<string>().ToList();
}
public override IsolationLevel DefaultIsolationLevel => IsolationLevel.ReadCommitted;
public override IEnumerable<ColumnInfo> GetColumnsInSchema(IDatabase db)
{
var items = db.Fetch<dynamic>("SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = (SELECT SCHEMA_NAME())");
@@ -246,6 +249,41 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName)
return result > 0;
}
public override void WriteLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted)
throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
db.Execute(@"SET LOCK_TIMEOUT 1800;");
var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
if (i == 0) // ensure we are actually locking!
throw new ArgumentException($"LockObject with id={lockId} does not exist.");
}
}
public override void ReadLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted)
throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.ExecuteScalar<int?>("SELECT value FROM umbracoLock WITH (REPEATABLEREAD) WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new ArgumentException($"LockObject with id={lockId} does not exist.", nameof(lockIds));
}
}
public override string FormatColumnRename(string tableName, string oldName, string newName)
{
return string.Format(RenameColumn, tableName, oldName, newName);

View File

@@ -200,7 +200,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return "NVARCHAR";
}
public abstract IsolationLevel DefaultIsolationLevel { get; }
public virtual IEnumerable<string> GetTablesInSchema(IDatabase db)
{
return new List<string>();
@@ -225,6 +227,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public abstract bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName);
public abstract void ReadLock(IDatabase db, params int[] lockIds);
public abstract void WriteLock(IDatabase db, params int[] lockIds);
public virtual bool DoesTableExist(IDatabase db, string tableName)
{
return false;

View File

@@ -20,9 +20,6 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public class UmbracoDatabase : Database, IUmbracoDatabase
{
// Umbraco's default isolation level is RepeatableRead
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
private readonly ILogger _logger;
private readonly RetryPolicy _connectionRetryPolicy;
private readonly RetryPolicy _commandRetryPolicy;
@@ -38,7 +35,7 @@ namespace Umbraco.Core.Persistence
/// <para>Also used by DatabaseBuilder for creating databases and installing/upgrading.</para>
/// </remarks>
public UmbracoDatabase(string connectionString, ISqlContext sqlContext, DbProviderFactory provider, ILogger logger, RetryPolicy connectionRetryPolicy = null, RetryPolicy commandRetryPolicy = null)
: base(connectionString, sqlContext.DatabaseType, provider, DefaultIsolationLevel)
: base(connectionString, sqlContext.DatabaseType, provider, sqlContext.SqlSyntax.DefaultIsolationLevel)
{
SqlContext = sqlContext;
@@ -54,7 +51,7 @@ namespace Umbraco.Core.Persistence
/// </summary>
/// <remarks>Internal for unit tests only.</remarks>
internal UmbracoDatabase(DbConnection connection, ISqlContext sqlContext, ILogger logger)
: base(connection, sqlContext.DatabaseType, DefaultIsolationLevel)
: base(connection, sqlContext.DatabaseType, sqlContext.SqlSyntax.DefaultIsolationLevel)
{
SqlContext = sqlContext;
_logger = logger;

View File

@@ -4,7 +4,6 @@ using System.Data.Common;
using System.Threading;
using NPoco;
using NPoco.FluentMappings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.FaultHandling;
using Umbraco.Core.Persistence.Mappers;
@@ -61,8 +60,8 @@ namespace Umbraco.Core.Persistence
/// <remarks>Used by the other ctor and in tests.</remarks>
public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy<IMapperCollection> mappers)
{
if (string.IsNullOrWhiteSpace(connectionStringName))
throw new ArgumentNullOrEmptyException(nameof(connectionStringName));
if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName));
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionStringName));
_mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.PropertyEditors
{
@@ -28,13 +27,15 @@ namespace Umbraco.Core.PropertyEditors
/// <param name="view">The view to use to render the field editor.</param>
public ConfigurationFieldAttribute(string key, string name, string view)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullOrEmptyException(nameof(key));
if (key == null) throw new ArgumentNullException(nameof(key));
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(key));
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (view == null) throw new ArgumentNullException(nameof(view));
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view));
Key = key;
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name;
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view));
View = view;
}
@@ -47,10 +48,12 @@ namespace Umbraco.Core.PropertyEditors
/// from the name of the property marked with this attribute.</remarks>
public ConfigurationFieldAttribute(string name, string view)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name;
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (view == null) throw new ArgumentNullException(nameof(view));
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view));
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view));
Name = name;
View = view;
}

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.PropertyEditors
{
@@ -53,17 +52,17 @@ namespace Umbraco.Core.PropertyEditors
/// </remarks>
public DataEditorAttribute(string alias, EditorType type, string name, string view)
{
if ((type & ~(EditorType.PropertyValue | EditorType.MacroParameter)) > 0)
throw new ArgumentOutOfRangeException(nameof(type), $"Not a valid {typeof(EditorType)} value.");
if (alias == null) throw new ArgumentNullException(nameof(alias));
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias));
if ((type & ~(EditorType.PropertyValue | EditorType.MacroParameter)) > 0) throw new ArgumentOutOfRangeException(nameof(type), type, $"Not a valid {typeof(EditorType)} value.");
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
if (view == null) throw new ArgumentNullException(nameof(view));
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(view));
Type = type;
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias));
Alias = alias;
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name;
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view));
View = view == NullView ? null : view;
}
@@ -100,8 +99,10 @@ namespace Umbraco.Core.PropertyEditors
get => _valueType;
set
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullOrEmptyException(nameof(value));
if (!ValueTypes.IsValue(value)) throw new ArgumentOutOfRangeException(nameof(value), $"Not a valid {typeof(ValueTypes)} value.");
if (value == null) throw new ArgumentNullException(nameof(value));
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(value));
if (!ValueTypes.IsValue(value)) throw new ArgumentOutOfRangeException(nameof(value), value, $"Not a valid {typeof(ValueTypes)} value.");
_valueType = value;
}
}

View File

@@ -18,6 +18,9 @@ namespace Umbraco.Core.PropertyEditors
[JsonProperty("name", Required = Required.Always)]
public string Name { get; set; }
[JsonProperty("nameTemplate")]
public string NameTemplate { get; set; }
[JsonProperty("alias", Required = Required.Always)]
public string Alias { get; set; }

View File

@@ -21,4 +21,4 @@ namespace Umbraco.Core.PropertyEditors
/// </remarks>
IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format);
}
}
}

View File

@@ -19,4 +19,4 @@ namespace Umbraco.Core.PropertyEditors
/// </remarks>
IEnumerable<ValidationResult> ValidateRequired(object value, string valueType);
}
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Services;
namespace Umbraco.Core.PropertyEditors.Validators
@@ -48,8 +47,9 @@ namespace Umbraco.Core.PropertyEditors.Validators
get => _regex;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullOrEmptyException(nameof(value));
if (value == null) throw new ArgumentNullException(nameof(value));
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(value));
_regex = value;
}
}
@@ -58,7 +58,9 @@ namespace Umbraco.Core.PropertyEditors.Validators
public IEnumerable<ValidationResult> Validate(object value, string valueType, object dataTypeConfiguration)
{
if (_regex == null)
{
throw new InvalidOperationException("The validator has not been configured.");
}
return ValidateFormat(value, valueType, _regex);
}
@@ -66,9 +68,12 @@ namespace Umbraco.Core.PropertyEditors.Validators
/// <inheritdoc cref="IValueFormatValidator.ValidateFormat"/>
public IEnumerable<ValidationResult> ValidateFormat(object value, string valueType, string format)
{
if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullOrEmptyException(nameof(format));
if (format == null) throw new ArgumentNullException(nameof(format));
if (string.IsNullOrWhiteSpace(format)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(format));
if (value == null || !new Regex(format).IsMatch(value.ToString()))
{
yield return new ValidationResult(_textService.Localize("validation", "invalidPattern"), new[] { "value" });
}
}
}
}

View File

@@ -35,14 +35,17 @@ namespace Umbraco.Core.PropertyEditors.Validators
{
if (value == null)
{
yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] {"value"});
yield return new ValidationResult(_textService.Localize("validation", "invalidNull"), new[] { "value" });
yield break;
}
if (valueType.InvariantEquals(ValueTypes.Json))
{
if (value.ToString().DetectIsEmptyJson())
{
yield return new ValidationResult(_textService.Localize("validation", "invalidEmpty"), new[] { "value" });
}
yield break;
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core
{
@@ -29,10 +28,14 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param>
/// <returns>A field getter function.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception>
/// <returns>
/// A field getter function.
/// </returns>
/// <exception cref="ArgumentNullException">fieldName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="fieldName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match field <typeparamref name="TDeclaring" />.<paramref name="fieldName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find field <typeparamref name="TDeclaring" />.<paramref name="fieldName" />.</exception>
public static Func<TDeclaring, TValue> EmitFieldGetter<TDeclaring, TValue>(string fieldName)
{
var field = GetField<TDeclaring, TValue>(fieldName);
@@ -45,10 +48,14 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param>
/// <returns>A field setter action.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception>
/// <returns>
/// A field setter action.
/// </returns>
/// <exception cref="ArgumentNullException">fieldName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="fieldName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match field <typeparamref name="TDeclaring" />.<paramref name="fieldName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find field <typeparamref name="TDeclaring" />.<paramref name="fieldName" />.</exception>
public static Action<TDeclaring, TValue> EmitFieldSetter<TDeclaring, TValue>(string fieldName)
{
var field = GetField<TDeclaring, TValue>(fieldName);
@@ -61,19 +68,36 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param>
/// <returns>A field getter and setter functions.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception>
/// <returns>
/// A field getter and setter functions.
/// </returns>
/// <exception cref="ArgumentNullException">fieldName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="fieldName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match field <typeparamref name="TDeclaring" />.<paramref name="fieldName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find field <typeparamref name="TDeclaring" />.<paramref name="fieldName" />.</exception>
public static (Func<TDeclaring, TValue>, Action<TDeclaring, TValue>) EmitFieldGetterAndSetter<TDeclaring, TValue>(string fieldName)
{
var field = GetField<TDeclaring, TValue>(fieldName);
return (EmitFieldGetter<TDeclaring, TValue>(field), EmitFieldSetter<TDeclaring, TValue>(field));
}
/// <summary>
/// Gets the field.
/// </summary>
/// <typeparam name="TDeclaring">The type of the declaring.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="fieldName">Name of the field.</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">fieldName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="fieldName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match field <typeparamref name="TDeclaring" />.<paramref name="fieldName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find field <typeparamref name="TDeclaring" />.<paramref name="fieldName" />.</exception>
private static FieldInfo GetField<TDeclaring, TValue>(string fieldName)
{
if (string.IsNullOrWhiteSpace(fieldName)) throw new ArgumentNullOrEmptyException(nameof(fieldName));
if (fieldName == null) throw new ArgumentNullException(nameof(fieldName));
if (string.IsNullOrWhiteSpace(fieldName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(fieldName));
// get the field
var field = typeof(TDeclaring).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
@@ -120,13 +144,18 @@ namespace Umbraco.Core
/// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="mustExist">A value indicating whether the property and its getter must exist.</param>
/// <returns>A property getter function. If <paramref name="mustExist"/> is <c>false</c>, returns null when the property or its getter does not exist.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the property or its getter does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception>
/// <returns>
/// A property getter function. If <paramref name="mustExist" /> is <c>false</c>, returns null when the property or its getter does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">propertyName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="propertyName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match property <typeparamref name="TDeclaring" />.<paramref name="propertyName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find property getter for <typeparamref name="TDeclaring" />.<paramref name="propertyName" />.</exception>
public static Func<TDeclaring, TValue> EmitPropertyGetter<TDeclaring, TValue>(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName));
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName));
var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
@@ -146,13 +175,18 @@ namespace Umbraco.Core
/// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="mustExist">A value indicating whether the property and its setter must exist.</param>
/// <returns>A property setter function. If <paramref name="mustExist"/> is <c>false</c>, returns null when the property or its setter does not exist.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the property or its setter does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception>
/// <returns>
/// A property setter function. If <paramref name="mustExist" /> is <c>false</c>, returns null when the property or its setter does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">propertyName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="propertyName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match property <typeparamref name="TDeclaring" />.<paramref name="propertyName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find property setter for <typeparamref name="TDeclaring" />.<paramref name="propertyName" />.</exception>
public static Action<TDeclaring, TValue> EmitPropertySetter<TDeclaring, TValue>(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName));
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName));
var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
@@ -172,13 +206,18 @@ namespace Umbraco.Core
/// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="mustExist">A value indicating whether the property and its getter and setter must exist.</param>
/// <returns>A property getter and setter functions. If <paramref name="mustExist"/> is <c>false</c>, returns null when the property or its getter or setter does not exist.</returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when the property or its getter or setter does not exist.</exception>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception>
/// <returns>
/// A property getter and setter functions. If <paramref name="mustExist" /> is <c>false</c>, returns null when the property or its getter or setter does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">propertyName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="propertyName" />
/// or
/// Value type <typeparamref name="TValue" /> does not match property <typeparamref name="TDeclaring" />.<paramref name="propertyName" /> type.</exception>
/// <exception cref="InvalidOperationException">Could not find property getter and setter for <typeparamref name="TDeclaring" />.<paramref name="propertyName" />.</exception>
public static (Func<TDeclaring, TValue>, Action<TDeclaring, TValue>) EmitPropertyGetterAndSetter<TDeclaring, TValue>(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentNullOrEmptyException(nameof(propertyName));
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyName));
var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
@@ -402,13 +441,17 @@ namespace Umbraco.Core
/// <typeparam name="TLambda">A lambda representing the method.</typeparam>
/// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</param>
/// <returns>The method. If <paramref name="mustExist"/> is <c>false</c>, returns null when the method does not exist.</returns>
/// <returns>
/// The method. If <paramref name="mustExist" /> is <c>false</c>, returns null when the method does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">methodName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="methodName" />
/// or
/// Occurs when <typeparamref name="TLambda" /> does not match the method signature..</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName" /> could be found.</exception>
/// <remarks>
/// <para>The method arguments are determined by <typeparamref name="TLambda"/> generic arguments.</para>
/// The method arguments are determined by <typeparamref name="TLambda" /> generic arguments.
/// </remarks>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="methodName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName"/> could be found.</exception>
/// <exception cref="ArgumentException">Occurs when Occurs when <typeparamref name="TLambda"/> does not match the method signature.</exception>
public static TLambda EmitMethod<TDeclaring, TLambda>(string methodName, bool mustExist = true)
{
return EmitMethod<TLambda>(typeof(TDeclaring), methodName, mustExist);
@@ -421,16 +464,21 @@ namespace Umbraco.Core
/// <param name="declaring">The declaring type.</param>
/// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</param>
/// <returns>The method. If <paramref name="mustExist"/> is <c>false</c>, returns null when the method does not exist.</returns>
/// <returns>
/// The method. If <paramref name="mustExist" /> is <c>false</c>, returns null when the method does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">methodName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="methodName" />
/// or
/// Occurs when <typeparamref name="TLambda" /> does not match the method signature..</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName" /> could be found.</exception>
/// <remarks>
/// <para>The method arguments are determined by <typeparamref name="TLambda"/> generic arguments.</para>
/// The method arguments are determined by <typeparamref name="TLambda" /> generic arguments.
/// </remarks>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="methodName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName"/> could be found.</exception>
/// <exception cref="ArgumentException">Occurs when Occurs when <typeparamref name="TLambda"/> does not match the method signature.</exception>
public static TLambda EmitMethod<TLambda>(Type declaring, string methodName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentNullOrEmptyException(nameof(methodName));
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(methodName));
var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda<TLambda>(true, out var isFunction);
@@ -510,17 +558,21 @@ namespace Umbraco.Core
/// <typeparam name="TLambda">A lambda representing the method.</typeparam>
/// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</param>
/// <returns>The method. If <paramref name="mustExist"/> is <c>false</c>, returns null when the method does not exist.</returns>
/// <returns>
/// The method. If <paramref name="mustExist" /> is <c>false</c>, returns null when the method does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">methodName</exception>
/// <exception cref="ArgumentException">Value can't be empty or consist only of white-space characters. - <paramref name="methodName" />
/// or
/// Occurs when <typeparamref name="TLambda" /> does not match the method signature..</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName" /> could be found.</exception>
/// <remarks>
/// <para>The method arguments are determined by <typeparamref name="TLambda"/> generic arguments.</para>
/// The method arguments are determined by <typeparamref name="TLambda" /> generic arguments.
/// </remarks>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="methodName"/> is null or empty.</exception>
/// <exception cref="InvalidOperationException">Occurs when no proper method with name <paramref name="methodName"/> could be found.</exception>
/// <exception cref="ArgumentException">Occurs when Occurs when <typeparamref name="TLambda"/> does not match the method signature.</exception>
public static TLambda EmitMethod<TLambda>(string methodName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(methodName))
throw new ArgumentNullOrEmptyException(nameof(methodName));
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(methodName));
// validate lambda type
var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda<TLambda>(false, out var isFunction);

View File

@@ -33,8 +33,6 @@ namespace Umbraco.Core.Scoping
private ICompletable _fscope;
private IEventDispatcher _eventDispatcher;
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
// initializes a new scope
private Scope(ScopeProvider scopeProvider,
ILogger logger, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable,
@@ -205,7 +203,7 @@ namespace Umbraco.Core.Scoping
{
if (_isolationLevel != IsolationLevel.Unspecified) return _isolationLevel;
if (ParentScope != null) return ParentScope.IsolationLevel;
return DefaultIsolationLevel;
return Database.SqlContext.SqlSyntax.DefaultIsolationLevel;
}
}
@@ -488,37 +486,9 @@ namespace Umbraco.Core.Scoping
?? (_logUncompletedScopes = Current.Configs.CoreDebug().LogUncompletedScopes)).Value;
/// <inheritdoc />
public void ReadLock(params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public void ReadLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.ReadLock(Database, lockIds);
/// <inheritdoc />
public void WriteLock(params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public void WriteLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.WriteLock(Database, lockIds);
}
}

View File

@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Web.Security;
using Microsoft.AspNet.Identity;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
@@ -217,7 +216,8 @@ namespace Umbraco.Core.Security
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user));
if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullOrEmptyException(nameof(passwordHash));
if (passwordHash == null) throw new ArgumentNullException(nameof(passwordHash));
if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentException("Value can't be empty.", nameof(passwordHash));
user.PasswordHash = passwordHash;
@@ -329,7 +329,7 @@ namespace Umbraco.Core.Security
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user));
if (login == null) throw new ArgumentNullException("login");
if (login == null) throw new ArgumentNullException(nameof(login));
var logins = user.Logins;
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id);
@@ -348,7 +348,7 @@ namespace Umbraco.Core.Security
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user));
if (login == null) throw new ArgumentNullException("login");
if (login == null) throw new ArgumentNullException(nameof(login));
var provider = login.LoginProvider;
var key = login.ProviderKey;
@@ -379,7 +379,7 @@ namespace Umbraco.Core.Security
public Task<BackOfficeIdentityUser> FindAsync(UserLoginInfo login)
{
ThrowIfDisposed();
if (login == null) throw new ArgumentNullException("login");
if (login == null) throw new ArgumentNullException(nameof(login));
//get all logins associated with the login id
var result = _externalLoginService.Find(login).ToArray();
@@ -413,7 +413,8 @@ namespace Umbraco.Core.Security
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName");
if (roleName == null) throw new ArgumentNullException(nameof(roleName));
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(roleName));
var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);
@@ -434,7 +435,8 @@ namespace Umbraco.Core.Security
{
ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName");
if (roleName == null) throw new ArgumentNullException(nameof(roleName));
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(roleName));
var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);

View File

@@ -25,7 +25,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets a content type.
/// </summary>
TItem Get(int id);
new TItem Get(int id);
/// <summary>
/// Gets a content type.
@@ -40,6 +40,7 @@ namespace Umbraco.Core.Services
int Count();
IEnumerable<TItem> GetAll(params int[] ids);
IEnumerable<TItem> GetAll(IEnumerable<Guid> ids);
IEnumerable<TItem> GetDescendants(int id, bool andSelf); // parent-child axis
IEnumerable<TItem> GetComposedOf(int id); // composition axis

View File

@@ -10,6 +10,13 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IDataTypeService : IService
{
/// <summary>
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id);
Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId);
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
EntityContainer GetContainer(int containerId);

View File

@@ -1848,7 +1848,7 @@ namespace Umbraco.Core.Services.Implement
scope.WriteLock(Constants.Locks.ContentTree);
var c = _documentRepository.Get(id);
if (c.VersionId != versionId) // don't delete the current version
if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version
_documentRepository.DeleteVersion(versionId);
scope.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId));
@@ -2885,7 +2885,8 @@ namespace Umbraco.Core.Services.Implement
private IContentType GetContentType(IScope scope, string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias));
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias));
scope.ReadLock(Constants.Locks.ContentTypes);
@@ -2900,7 +2901,8 @@ namespace Umbraco.Core.Services.Implement
private IContentType GetContentType(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
if (contentTypeAlias == null) throw new ArgumentNullException(nameof(contentTypeAlias));
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(contentTypeAlias));
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
@@ -3083,7 +3085,7 @@ namespace Umbraco.Core.Services.Implement
var version = GetVersion(versionId);
//Good ole null checks
if (content == null || version == null)
if (content == null || version == null || content.Trashed)
{
return new OperationResult(OperationResultType.FailedCannot, evtMsgs);
}

View File

@@ -252,12 +252,12 @@ namespace Umbraco.Core.Services.Implement
}
}
public IEnumerable<TItem> GetAll(params Guid[] ids)
public IEnumerable<TItem> GetAll(IEnumerable<Guid> ids)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(ReadLockIds);
return Repository.GetMany(ids);
return Repository.GetMany(ids.ToArray());
}
}
@@ -595,10 +595,9 @@ namespace Umbraco.Core.Services.Implement
public TItem Copy(TItem original, string alias, string name, TItem parent)
{
if (original == null) throw new ArgumentNullException(nameof(original));
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias));
if (parent != null && parent.HasIdentity == false)
throw new InvalidOperationException("Parent must have an identity.");
if (alias == null) throw new ArgumentNullException(nameof(alias));
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias));
if (parent != null && parent.HasIdentity == false) throw new InvalidOperationException("Parent must have an identity.");
// this is illegal
//var originalb = (ContentTypeCompositionBase)original;

View File

@@ -466,6 +466,14 @@ namespace Umbraco.Core.Services.Implement
}
}
public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
{
using (var scope = ScopeProvider.CreateScope(autoComplete:true))
{
return _dataTypeRepository.FindUsages(id);
}
}
private void Audit(AuditType type, int userId, int objectId)
{
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));

View File

@@ -360,7 +360,9 @@ namespace Umbraco.Core.Services.Implement
new XElement("Definition", definition.Key),
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
new XElement("Mandatory", propertyType.Mandatory.ToString()),
new XElement("MandatoryMessage", propertyType.MandatoryMessage),
new XElement("Validation", propertyType.ValidationRegExp),
new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage),
new XElement("Description", new XCData(propertyType.Description)));
genericProperties.Add(genericProperty);
}
@@ -487,7 +489,9 @@ namespace Umbraco.Core.Services.Implement
new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name),
new XElement("SortOrder", propertyType.SortOrder),
new XElement("Mandatory", propertyType.Mandatory.ToString()),
propertyType.MandatoryMessage != null ? new XElement("MandatoryMessage", propertyType.MandatoryMessage) : null,
propertyType.ValidationRegExp != null ? new XElement("Validation", propertyType.ValidationRegExp) : null,
propertyType.ValidationRegExpMessage != null ? new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage) : null,
propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null,
new XElement("Variations", propertyType.Variations.ToString()));

View File

@@ -405,6 +405,21 @@ namespace Umbraco.Core.Services.Implement
/// <returns></returns>
public ITemplate CreateTemplateWithIdentity(string name, string alias, string content, ITemplate masterTemplate = null, int userId = Constants.Security.SuperUserId)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name cannot be empty or contain only white-space characters", nameof(name));
}
if (name.Length > 255)
{
throw new ArgumentOutOfRangeException(nameof(name), "Name cannot be more than 255 characters in length.");
}
// file might already be on disk, if so grab the content to avoid overwriting
var template = new Template(name, alias)
{
@@ -539,6 +554,17 @@ namespace Umbraco.Core.Services.Implement
/// <param name="userId"></param>
public void SaveTemplate(ITemplate template, int userId = Constants.Security.SuperUserId)
{
if (template == null)
{
throw new ArgumentNullException(nameof(template));
}
if (string.IsNullOrWhiteSpace(template.Name) || template.Name.Length > 255)
{
throw new InvalidOperationException("Name cannot be null, empty, contain only white-space characters or be more than 255 characters in length.");
}
using (var scope = ScopeProvider.CreateScope())
{
if (scope.Events.DispatchCancelable(SavingTemplate, this, new SaveEventArgs<ITemplate>(template)))

Some files were not shown because too many files have changed in this diff Show More