Merge remote-tracking branch 'origin/v8/dev' into v8/feature/media-tracking

# Conflicts:
#	src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
#	src/Umbraco.Core/Models/RelationType.cs
#	src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
#	src/Umbraco.Web/UmbracoComponentRenderer.cs
This commit is contained in:
Shannon
2019-12-18 15:28:27 +11:00
275 changed files with 4520 additions and 3266 deletions

4
.github/BUILD.md vendored
View File

@@ -39,6 +39,8 @@ To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the
build/build.ps1 build/build.ps1
If you only see a build.bat-file, you're probably on the wrong branch. If you switch to the correct branch (dev-v8) the file will appear and you can build it.
You might run into [Powershell quirks](#powershell-quirks). You might run into [Powershell quirks](#powershell-quirks).
### Build Infrastructure ### Build Infrastructure
@@ -209,4 +211,4 @@ The best solution is to unblock the Zip file before un-zipping: right-click the
### Git Quirks ### Git Quirks
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details). Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).

View File

@@ -59,7 +59,8 @@ Great question! The short version goes like this:
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool * **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png) ![Clone the fork](img/clonefork.png)
* **Switch to the correct branch** - switch to the v8-dev branch
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md) * **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions) * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first. * **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first.

View File

@@ -11,17 +11,12 @@ Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
Don't forget to build! Don't forget to build!
We've done our best to transform your configuration files but in case something is not quite right: remember we We've done our best to transform your configuration files but in case something is not quite right: we recommmend you look in source control for the previous version so you can find the original files before they were transformed.
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
We've overwritten all the files in the Umbraco folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
sure to do a compare and merge with the NuGetBackup folder.
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you. Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, config\splashes and global.asax. system: umbraco, config\splashes and global.asax.

View File

@@ -456,10 +456,10 @@
$ubuild.DefineMethod("PrepareAngularDocs", $ubuild.DefineMethod("PrepareAngularDocs",
{ {
Write-Host "Prepare Angular Documentation" Write-Host "Prepare Angular Documentation"
$src = "$($this.SolutionRoot)\src" $src = "$($this.SolutionRoot)\src"
$out = $this.BuildOutput $out = $this.BuildOutput
"Moving to Umbraco.Web.UI.Docs folder" "Moving to Umbraco.Web.UI.Docs folder"
cd ..\src\Umbraco.Web.UI.Docs cd ..\src\Umbraco.Web.UI.Docs

View File

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

View File

@@ -70,7 +70,23 @@ namespace Umbraco.Core.Collections
/// The number of elements contained in the <see cref="T:System.Collections.ICollection"/>. /// The number of elements contained in the <see cref="T:System.Collections.ICollection"/>.
/// </returns> /// </returns>
/// <filterpriority>2</filterpriority> /// <filterpriority>2</filterpriority>
public int Count => GetThreadSafeClone().Count; public int Count
{
get
{
try
{
_instanceLocker.EnterReadLock();
return _innerSet.Count;
}
finally
{
if (_instanceLocker.IsReadLockHeld)
_instanceLocker.ExitReadLock();
}
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
@@ -105,8 +121,7 @@ namespace Umbraco.Core.Collections
/// <returns></returns> /// <returns></returns>
public bool TryAdd(T item) public bool TryAdd(T item)
{ {
var clone = GetThreadSafeClone(); if (Contains(item)) return false;
if (clone.Contains(item)) return false;
try try
{ {
_instanceLocker.EnterWriteLock(); _instanceLocker.EnterWriteLock();
@@ -150,7 +165,16 @@ namespace Umbraco.Core.Collections
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param> /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
public bool Contains(T item) public bool Contains(T item)
{ {
return GetThreadSafeClone().Contains(item); try
{
_instanceLocker.EnterReadLock();
return _innerSet.Contains(item);
}
finally
{
if (_instanceLocker.IsReadLockHeld)
_instanceLocker.ExitReadLock();
}
} }
/// <summary> /// <summary>
@@ -168,13 +192,13 @@ namespace Umbraco.Core.Collections
HashSet<T> clone = null; HashSet<T> clone = null;
try try
{ {
_instanceLocker.EnterWriteLock(); _instanceLocker.EnterReadLock();
clone = new HashSet<T>(_innerSet, _innerSet.Comparer); clone = new HashSet<T>(_innerSet, _innerSet.Comparer);
} }
finally finally
{ {
if (_instanceLocker.IsWriteLockHeld) if (_instanceLocker.IsReadLockHeld)
_instanceLocker.ExitWriteLock(); _instanceLocker.ExitReadLock();
} }
return clone; return clone;
} }

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Collections 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> /// <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) 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); Indecies = new Dictionary<TKey, int>(equalityComparer);
} }
@@ -36,7 +37,7 @@ namespace Umbraco.Core.Collections
{ {
var key = KeySelector(item); var key = KeySelector(item);
if (Indecies.ContainsKey(key)) 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) if (index != Count)
{ {
@@ -91,7 +92,7 @@ namespace Umbraco.Core.Collections
{ {
//confirm key matches //confirm key matches
if (!KeySelector(value).Equals(key)) 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)) if (!Indecies.ContainsKey(key))
{ {
@@ -118,7 +119,7 @@ namespace Umbraco.Core.Collections
//confirm key matches //confirm key matches
if (!KeySelector(value).Equals(key)) 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; this[Indecies[key]] = value;
return true; return true;
@@ -155,12 +156,12 @@ namespace Umbraco.Core.Collections
{ {
if (!Indecies.ContainsKey(currentKey)) 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)) 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]; var currentIndex = Indecies[currentKey];
@@ -234,16 +235,5 @@ namespace Umbraco.Core.Collections
} }
#endregion #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

@@ -18,19 +18,52 @@ namespace Umbraco.Core.Composing
private readonly Composition _composition; private readonly Composition _composition;
private readonly IProfilingLogger _logger; private readonly IProfilingLogger _logger;
private readonly IEnumerable<Type> _composerTypes; private readonly IEnumerable<Type> _composerTypes;
private readonly IEnumerable<Attribute> _enableDisableAttributes;
private const int LogThresholdMilliseconds = 100; private const int LogThresholdMilliseconds = 100;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Composers"/> class. /// Initializes a new instance of the <see cref="Composers" /> class.
/// </summary> /// </summary>
/// <param name="composition">The composition.</param> /// <param name="composition">The composition.</param>
/// <param name="composerTypes">The composer types.</param> /// <param name="composerTypes">The <see cref="IComposer" /> types.</param>
/// <param name="logger">A profiling logger.</param> /// <param name="logger">The profiling logger.</param>
[Obsolete("This overload only gets the EnableComposer/DisableComposer attributes from the composerTypes assemblies.")]
public Composers(Composition composition, IEnumerable<Type> composerTypes, IProfilingLogger logger) public Composers(Composition composition, IEnumerable<Type> composerTypes, IProfilingLogger logger)
: this(composition, composerTypes, Enumerable.Empty<Attribute>(), logger)
{
var enableDisableAttributes = new List<Attribute>();
var assemblies = composerTypes.Select(t => t.Assembly).Distinct();
foreach (var assembly in assemblies)
{
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(EnableComposerAttribute)));
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(DisableComposerAttribute)));
}
_enableDisableAttributes = enableDisableAttributes;
}
/// <summary>
/// Initializes a new instance of the <see cref="Composers" /> class.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="composerTypes">The <see cref="IComposer" /> types.</param>
/// <param name="enableDisableAttributes">The <see cref="EnableComposerAttribute" /> and/or <see cref="DisableComposerAttribute" /> attributes.</param>
/// <param name="logger">The profiling logger.</param>
/// <exception cref="ArgumentNullException">composition
/// or
/// composerTypes
/// or
/// enableDisableAttributes
/// or
/// logger</exception>
public Composers(Composition composition, IEnumerable<Type> composerTypes, IEnumerable<Attribute> enableDisableAttributes, IProfilingLogger logger)
{ {
_composition = composition ?? throw new ArgumentNullException(nameof(composition)); _composition = composition ?? throw new ArgumentNullException(nameof(composition));
_composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes));
_enableDisableAttributes = enableDisableAttributes ?? throw new ArgumentNullException(nameof(enableDisableAttributes));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
} }
@@ -103,7 +136,7 @@ namespace Umbraco.Core.Composing
.ToList(); .ToList();
// enable or disable composers // enable or disable composers
EnableDisableComposers(composerTypeList); EnableDisableComposers(_enableDisableAttributes, composerTypeList);
void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2) void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2)
where TAttribute : Attribute where TAttribute : Attribute
@@ -218,7 +251,7 @@ namespace Umbraco.Core.Composing
return text.ToString(); return text.ToString();
} }
private static void EnableDisableComposers(ICollection<Type> types) private static void EnableDisableComposers(IEnumerable<Attribute> enableDisableAttributes, ICollection<Type> types)
{ {
var enabled = new Dictionary<Type, EnableInfo>(); var enabled = new Dictionary<Type, EnableInfo>();
@@ -240,20 +273,16 @@ namespace Umbraco.Core.Composing
enableInfo.Weight = weight2; enableInfo.Weight = weight2;
} }
var assemblies = types.Select(x => x.Assembly).Distinct(); foreach (var attr in enableDisableAttributes.OfType<EnableComposerAttribute>())
foreach (var assembly in assemblies)
{ {
foreach (var attr in assembly.GetCustomAttributes<EnableComposerAttribute>()) var type = attr.EnabledType;
{ UpdateEnableInfo(type, 2, enabled, true);
var type = attr.EnabledType; }
UpdateEnableInfo(type, 2, enabled, true);
}
foreach (var attr in assembly.GetCustomAttributes<DisableComposerAttribute>()) foreach (var attr in enableDisableAttributes.OfType<DisableComposerAttribute>())
{ {
var type = attr.DisabledType; var type = attr.DisabledType;
UpdateEnableInfo(type, 2, enabled, false); UpdateEnableInfo(type, 2, enabled, false);
}
} }
foreach (var composerType in types) foreach (var composerType in types)

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.Serialization;
using System.Text; using System.Text;
namespace Umbraco.Core.Composing.LightInject namespace Umbraco.Core.Composing.LightInject
@@ -6,20 +7,51 @@ namespace Umbraco.Core.Composing.LightInject
/// <summary> /// <summary>
/// Represents errors that occur due to LightInject. /// Represents errors that occur due to LightInject.
/// </summary> /// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class LightInjectException : Exception 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 LightInjectUnableToResolveType = "Unable to resolve type:";
private const string LightInjectUnresolvedDependency = "Unresolved dependency "; private const string LightInjectUnresolvedDependency = "Unresolved dependency ";
private const string LightInjectRequestedDependency = "[Requested 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) public static void TryThrow(Exception e)
{ {
var ex = e as InvalidOperationException; var ex = e as InvalidOperationException;
@@ -32,6 +64,12 @@ namespace Umbraco.Core.Composing.LightInject
throw new LightInjectException(sb.ToString(), e); 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) public static void TryThrow(Exception e, Type implementingType)
{ {
var ex = e as InvalidOperationException; var ex = e as InvalidOperationException;
@@ -45,6 +83,11 @@ namespace Umbraco.Core.Composing.LightInject
throw new LightInjectException(sb.ToString(), e); 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) private static void WriteDetails(InvalidOperationException ex, StringBuilder sb)
{ {
ex = ex.InnerException as InvalidOperationException; ex = ex.InnerException as InvalidOperationException;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Web; using System.Web;
@@ -505,6 +506,49 @@ namespace Umbraco.Core.Composing
#endregion #endregion
#region Get Assembly Attributes
/// <summary>
/// Gets the assembly attributes of the specified type <typeparamref name="T" />.
/// </summary>
/// <typeparam name="T">The attribute type.</typeparam>
/// <returns>
/// The assembly attributes of the specified type <typeparamref name="T" />.
/// </returns>
public IEnumerable<T> GetAssemblyAttributes<T>()
where T : Attribute
{
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes<T>()).ToList();
}
/// <summary>
/// Gets all the assembly attributes.
/// </summary>
/// <returns>
/// All assembly attributes.
/// </returns>
public IEnumerable<Attribute> GetAssemblyAttributes()
{
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList();
}
/// <summary>
/// Gets the assembly attributes of the specified <paramref name="attributeTypes" />.
/// </summary>
/// <param name="attributeTypes">The attribute types.</param>
/// <returns>
/// The assembly attributes of the specified types.
/// </returns>
/// <exception cref="ArgumentNullException">attributeTypes</exception>
public IEnumerable<Attribute> GetAssemblyAttributes(params Type[] attributeTypes)
{
if (attributeTypes == null) throw new ArgumentNullException(nameof(attributeTypes));
return AssembliesToScan.SelectMany(a => attributeTypes.SelectMany(at => a.GetCustomAttributes(at))).ToList();
}
#endregion
#region Get Types #region Get Types
/// <summary> /// <summary>
@@ -813,11 +857,44 @@ namespace Umbraco.Core.Composing
} }
/// <summary> /// <summary>
/// Represents the error that occurs when a type was not found in the cache type /// Represents the error that occurs when a type was not found in the cache type list with the specified TypeResolutionKind.
/// list with the specified TypeResolutionKind.
/// </summary> /// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
internal class CachedTypeNotFoundInFileException : Exception 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 #endregion
} }

View File

@@ -221,7 +221,8 @@ namespace Umbraco.Core
FailedPasswordAttempts, FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, 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, LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate) new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{ {
Name = LastLockoutDateLabel Name = LastLockoutDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
} }
}, },
{ {
LastLoginDate, LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate) new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{ {
Name = LastLoginDateLabel Name = LastLoginDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
} }
}, },
{ {
LastPasswordChangeDate, LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate) new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{ {
Name = LastPasswordChangeDateLabel Name = LastPasswordChangeDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
} }
}, },
{ {
PasswordAnswer, PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer) new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{ {
Name = PasswordAnswerLabel Name = PasswordAnswerLabel,
DataTypeId = Constants.DataTypes.LabelString
} }
}, },
{ {
PasswordQuestion, PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion) new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{ {
Name = PasswordQuestionLabel Name = PasswordQuestionLabel,
DataTypeId = Constants.DataTypes.LabelString
} }
} }
}; };

View File

@@ -13,6 +13,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Membership;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services; using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement; using Umbraco.Core.Services.Implement;
@@ -23,6 +24,8 @@ namespace Umbraco.Core
// this ain't pretty // this ain't pretty
private static IMediaFileSystem _mediaFileSystem; private static IMediaFileSystem _mediaFileSystem;
private static IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem); private static IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem);
private static readonly PropertyEditorCollection _propertyEditors;
private static PropertyEditorCollection PropertyEditors = _propertyEditors ?? (_propertyEditors = Current.PropertyEditors);
#region IContent #region IContent
@@ -162,14 +165,12 @@ namespace Umbraco.Core
// Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an // Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an
// existing IMedia with extension SetValue causes exception 'Illegal characters in path' // existing IMedia with extension SetValue causes exception 'Illegal characters in path'
string oldpath = null; string oldpath = null;
if (property.GetValue(culture, segment) is string svalue) var value = property.GetValue(culture, segment);
if (PropertyEditors.TryGet(propertyTypeAlias, out var editor)
&& editor is IDataEditorWithMediaPath dataEditor)
{ {
if (svalue.DetectIsJson()) var svalue = dataEditor.GetMediaPath(value);
{
// the property value is a JSON serialized image crop data set - grab the "src" property as the file source
var jObject = JsonConvert.DeserializeObject<JObject>(svalue);
svalue = jObject != null ? jObject.GetValueAsString("src") : svalue;
}
oldpath = MediaFileSystem.GetRelativePath(svalue); oldpath = MediaFileSystem.GetRelativePath(svalue);
} }

View File

@@ -41,5 +41,43 @@ namespace Umbraco.Core
return (num & nums) > 0; return (num & nums) > 0;
} }
/// <summary>
/// Sets a flag of the given input enum
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="input">Enum to set flag of</param>
/// <param name="flag">Flag to set</param>
/// <returns>A new enum with the flag set</returns>
public static T SetFlag<T>(this T input, T flag)
where T : Enum
{
var i = Convert.ToUInt64(input);
var f = Convert.ToUInt64(flag);
// bitwise OR to set flag f of enum i
var result = i | f;
return (T)Enum.ToObject(typeof(T), result);
}
/// <summary>
/// Unsets a flag of the given input enum
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="input">Enum to unset flag of</param>
/// <param name="flag">Flag to unset</param>
/// <returns>A new enum with the flag unset</returns>
public static T UnsetFlag<T>(this T input, T flag)
where T : Enum
{
var i = Convert.ToUInt64(input);
var f = Convert.ToUInt64(flag);
// bitwise AND combined with bitwise complement to unset flag f of enum i
var result = i & ~f;
return (T)Enum.ToObject(typeof(T), result);
}
} }
} }

View File

@@ -1,16 +1,24 @@
using System; using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions namespace Umbraco.Core.Exceptions
{ {
/// <summary> /// <summary>
/// The exception that is thrown when a null reference, or an empty 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.
/// is passed to a method that does not accept it as a valid argument.
/// </summary> /// </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 public class ArgumentNullOrEmptyException : ArgumentNullException
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class /// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
/// with the name of the parameter that caused this exception. /// </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> /// </summary>
/// <param name="paramName">The named of the parameter that caused the exception.</param> /// <param name="paramName">The named of the parameter that caused the exception.</param>
public ArgumentNullOrEmptyException(string paramName) public ArgumentNullOrEmptyException(string paramName)
@@ -18,13 +26,30 @@ namespace Umbraco.Core.Exceptions
{ } { }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class /// 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.
/// with a specified error message and the name of the parameter that caused this exception.
/// </summary> /// </summary>
/// <param name="paramName">The named of the parameter that caused the exception.</param> /// <param name="paramName">The named of the parameter that caused the exception.</param>
/// <param name="message">A message that describes the error.</param> /// <param name="message">A message that describes the error.</param>
public ArgumentNullOrEmptyException(string paramName, string message) public ArgumentNullOrEmptyException(string paramName, string message)
: base(paramName, 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;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions namespace Umbraco.Core.Exceptions
{ {
/// <summary>
/// The exception that is thrown when authorization failed.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class AuthorizationException : Exception public class AuthorizationException : Exception
{ {
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
/// </summary>
public AuthorizationException() 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) public AuthorizationException(string message)
: base(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;
using System.Runtime.Serialization;
using System.Text; using System.Text;
namespace Umbraco.Core.Exceptions namespace Umbraco.Core.Exceptions
@@ -6,6 +7,8 @@ namespace Umbraco.Core.Exceptions
/// <summary> /// <summary>
/// An exception that is thrown if the Umbraco application cannot boot. /// An exception that is thrown if the Umbraco application cannot boot.
/// </summary> /// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class BootFailedException : Exception public class BootFailedException : Exception
{ {
/// <summary> /// <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."; public const string DefaultMessage = "Boot failed: Umbraco cannot run. See Umbraco's log file for more details.";
/// <summary> /// <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> /// </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) public BootFailedException(string message)
: base(message) : base(message)
{ } { }
/// <summary> /// <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. /// and a reference to the inner exception which is the cause of this exception.
/// </summary> /// </summary>
/// <param name="message">The message that describes the error. </param> /// <param name="message">The message that describes the error.</param>
/// <param name="inner">The inner exception, or null.</param> /// <param name="innerException">The inner exception, or null.</param>
public BootFailedException(string message, Exception inner) public BootFailedException(string message, Exception innerException)
: base(message, inner) : base(message, innerException)
{ } { }
/// <summary> /// <summary>
/// Rethrows a captured <see cref="BootFailedException"/>. /// Initializes a new instance of the <see cref="BootFailedException" /> class.
/// </summary> /// </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) public static void Rethrow(BootFailedException bootFailedException)
{ {
if (bootFailedException == null) 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;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions namespace Umbraco.Core.Exceptions
{ {
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <seealso cref="System.Exception" />
[Serializable]
internal class DataOperationException<T> : Exception 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; } 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) public DataOperationException(T operation, string message)
: base(message) : base(message)
{ {
Operation = operation; Operation = operation;
} }
public DataOperationException(T operation) /// <summary>
: base("Data operation exception: " + operation) /// 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;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions 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 class InvalidCompositionException : Exception
{ {
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass) /// <summary>
{ /// Gets the content type alias.
ContentTypeAlias = contentTypeAlias; /// </summary>
AddedCompositionAlias = addedCompositionAlias; /// <value>
PropertyTypeAliases = propertyTypeAliass; /// The content type alias.
} /// </value>
public string ContentTypeAlias { get; }
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass) /// <summary>
{ /// Gets the added composition alias.
ContentTypeAlias = contentTypeAlias; /// </summary>
PropertyTypeAliases = propertyTypeAliass; /// <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 /// <summary>
{ /// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
get /// </summary>
{ /// <param name="contentTypeAlias">The content type alias.</param>
return AddedCompositionAlias.IsNullOrWhiteSpace() /// <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( ? string.Format(
"ContentType with alias '{0}' has an invalid composition " + "ContentType with alias '{0}' has an invalid composition " +
"and there was a conflict on the following PropertyTypes: '{1}'. " + "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.", "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( : string.Format(
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " + "ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
"but there was a conflict on the following PropertyTypes: '{2}'. " + "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.", "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 namespace Umbraco.Core.Exceptions
{ {
/// <summary> /// <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> /// </summary>
/// <seealso cref="System.Exception" />
[Serializable] [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()
{ { }
}
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;
using System.Runtime.Serialization;
namespace Umbraco.Core.Exceptions namespace Umbraco.Core.Exceptions
{ {
/// <summary> /// <summary>
/// The exception that is thrown when a requested method or operation is not, and will not be, implemented. /// The exception that is thrown when a requested method or operation is not, and will not be, implemented.
/// </summary> /// </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. /// 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 /// 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 public class WontImplementException : NotImplementedException
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WontImplementException"/> class. /// Initializes a new instance of the <see cref="WontImplementException" /> class.
/// </summary> /// </summary>
public WontImplementException() public WontImplementException()
{ } { }
/// <summary> /// <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> /// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public WontImplementException(string message) public WontImplementException(string message)
: base(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

@@ -14,6 +14,9 @@ namespace Umbraco.Core
/// <summary> /// <summary>
/// Gets a value indicating whether the current domain is the main domain. /// Gets a value indicating whether the current domain is the main domain.
/// </summary> /// </summary>
/// <remarks>
/// When the first call is made to this there will generally be some logic executed to acquire a distributed lock lease.
/// </remarks>
bool IsMainDom { get; } bool IsMainDom { get; }
/// <summary> /// <summary>
@@ -35,4 +38,4 @@ namespace Umbraco.Core
/// is guaranteed to execute before the AppDomain releases the main domain status.</remarks> /// is guaranteed to execute before the AppDomain releases the main domain status.</remarks>
bool Register(Action install, Action release, int weight = 100); bool Register(Action install, Action release, int weight = 100);
} }
} }

View File

@@ -1,20 +1,46 @@
using System; using System;
using System.Collections.Generic; using System.Runtime.Serialization;
using System.Linq;
using System.Text;
namespace Umbraco.Core.IO 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 public class FileSecurityException : Exception
{ {
/// <summary>
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
/// </summary>
public FileSecurityException() 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Configuration;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Media;
using Umbraco.Core.Models; using Umbraco.Core.Models;
namespace Umbraco.Core.IO namespace Umbraco.Core.IO
@@ -92,7 +87,8 @@ namespace Umbraco.Core.IO
{ {
if (content == null) throw new ArgumentNullException(nameof(content)); if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); 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)); if (filestream == null) throw new ArgumentNullException(nameof(filestream));
// clear the old file, if any // clear the old file, if any
@@ -111,7 +107,8 @@ namespace Umbraco.Core.IO
{ {
if (content == null) throw new ArgumentNullException(nameof(content)); if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); 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 // ensure we have a file to copy
if (FileExists(sourcepath) == false) return null; if (FileExists(sourcepath) == false) return null;

View File

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

View File

@@ -15,25 +15,26 @@ namespace Umbraco.Core
/// <para>When an AppDomain starts, it tries to acquire the main domain status.</para> /// <para>When an AppDomain starts, it tries to acquire the main domain status.</para>
/// <para>When an AppDomain stops (eg the application is restarting) it should release the main domain status.</para> /// <para>When an AppDomain stops (eg the application is restarting) it should release the main domain status.</para>
/// </remarks> /// </remarks>
internal class MainDom : IMainDom, IRegisteredObject internal class MainDom : IMainDom, IRegisteredObject, IDisposable
{ {
#region Vars #region Vars
private readonly ILogger _logger; private readonly ILogger _logger;
// our own lock for local consistency // our own lock for local consistency
private readonly object _locko = new object(); private object _locko = new object();
// async lock representing the main domain lock // async lock representing the main domain lock
private readonly AsyncLock _asyncLock; private readonly SystemLock _systemLock;
private IDisposable _asyncLocker; private IDisposable _systemLocker;
// event wait handle used to notify current main domain that it should // event wait handle used to notify current main domain that it should
// release the lock because a new domain wants to be the main domain // release the lock because a new domain wants to be the main domain
private readonly EventWaitHandle _signal; private readonly EventWaitHandle _signal;
private bool _isInitialized;
// indicates whether... // indicates whether...
private volatile bool _isMainDom; // we are the main domain private bool _isMainDom; // we are the main domain
private volatile bool _signaled; // we have been signaled private volatile bool _signaled; // we have been signaled
// actions to run before releasing the main domain // actions to run before releasing the main domain
@@ -48,13 +49,13 @@ namespace Umbraco.Core
// initializes a new instance of MainDom // initializes a new instance of MainDom
public MainDom(ILogger logger) public MainDom(ILogger logger)
{ {
HostingEnvironment.RegisterObject(this);
_logger = logger; _logger = logger;
var appId = string.Empty;
// HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
if (HostingEnvironment.ApplicationID != null) var appId = HostingEnvironment.ApplicationID?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty;
appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
// combining with the physical path because if running on eg IIS Express, // combining with the physical path because if running on eg IIS Express,
// two sites could have the same appId even though they are different. // two sites could have the same appId even though they are different.
// //
@@ -64,11 +65,11 @@ namespace Umbraco.Core
// we *cannot* use the process ID here because when an AppPool restarts it is // we *cannot* use the process ID here because when an AppPool restarts it is
// a new process for the same application path // a new process for the same application path
var appPath = HostingEnvironment.ApplicationPhysicalPath; var appPath = HostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty;
var hash = (appId + ":::" + appPath).GenerateHash<SHA1>(); var hash = (appId + ":::" + appPath).GenerateHash<SHA1>();
var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK";
_asyncLock = new AsyncLock(lockName); _systemLock = new SystemLock(lockName);
var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT"; var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT";
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
@@ -99,6 +100,12 @@ namespace Umbraco.Core
lock (_locko) lock (_locko)
{ {
if (_signaled) return false; if (_signaled) return false;
if (_isMainDom == false)
{
_logger.Warn<MainDom>("Register called when MainDom has not been acquired");
return false;
}
install?.Invoke(); install?.Invoke();
if (release != null) if (release != null)
_callbacks.Add(new KeyValuePair<int, Action>(weight, release)); _callbacks.Add(new KeyValuePair<int, Action>(weight, release));
@@ -118,64 +125,65 @@ namespace Umbraco.Core
if (_signaled) return; if (_signaled) return;
if (_isMainDom == false) return; // probably not needed if (_isMainDom == false) return; // probably not needed
_signaled = true; _signaled = true;
}
try try
{
_logger.Info<MainDom>("Stopping ({SignalSource})", source);
foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value))
{ {
try _logger.Info<MainDom>("Stopping ({SignalSource})", source);
foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value))
{ {
callback(); // no timeout on callbacks try
{
callback(); // no timeout on callbacks
}
catch (Exception e)
{
_logger.Error<MainDom>(e, "Error while running callback");
continue;
}
} }
catch (Exception e) _logger.Debug<MainDom>("Stopped ({SignalSource})", source);
{
_logger.Error<MainDom>(e, "Error while running callback, remaining callbacks will not run.");
throw;
}
} }
_logger.Debug<MainDom>("Stopped ({SignalSource})", source); finally
} {
finally // in any case...
{ _isMainDom = false;
// in any case... _systemLocker?.Dispose();
_isMainDom = false; _logger.Info<MainDom>("Released ({SignalSource})", source);
_asyncLocker.Dispose(); }
_logger.Info<MainDom>("Released ({SignalSource})", source);
} }
} }
// acquires the main domain // acquires the main domain
internal bool Acquire() private bool Acquire()
{ {
lock (_locko) // we don't want the hosting environment to interfere by signaling // if signaled, too late to acquire, give up
// the handler is not installed so that would be the hosting environment
if (_signaled)
{ {
// if signaled, too late to acquire, give up _logger.Info<MainDom>("Cannot acquire (signaled).");
// the handler is not installed so that would be the hosting environment return false;
if (_signaled) }
{
_logger.Info<MainDom>("Cannot acquire (signaled).");
return false;
}
_logger.Info<MainDom>("Acquiring."); _logger.Info<MainDom>("Acquiring.");
// signal other instances that we want the lock, then wait one the lock, // signal other instances that we want the lock, then wait one the lock,
// which may timeout, and this is accepted - see comments below // which may timeout, and this is accepted - see comments below
// signal, then wait for the lock, then make sure the event is // signal, then wait for the lock, then make sure the event is
// reset (maybe there was noone listening..) // reset (maybe there was noone listening..)
_signal.Set(); _signal.Set();
// if more than 1 instance reach that point, one will get the lock // if more than 1 instance reach that point, one will get the lock
// and the other one will timeout, which is accepted // and the other one will timeout, which is accepted
//TODO: This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset?
_asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds);
_isMainDom = true;
//This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset.
try
{
_systemLocker = _systemLock.Lock(LockTimeoutMilliseconds);
}
finally
{
// we need to reset the event, because otherwise we would end up // we need to reset the event, because otherwise we would end up
// signaling ourselves and committing suicide immediately. // signaling ourselves and committing suicide immediately.
// only 1 instance can reach that point, but other instances may // only 1 instance can reach that point, but other instances may
@@ -183,35 +191,58 @@ namespace Umbraco.Core
// which is accepted // which is accepted
_signal.Reset(); _signal.Reset();
//WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
_signal.WaitOneAsync()
.ContinueWith(_ => OnSignal("signal"));
HostingEnvironment.RegisterObject(this);
_logger.Info<MainDom>("Acquired.");
return true;
} }
//WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
_signal.WaitOneAsync()
.ContinueWith(_ => OnSignal("signal"));
_logger.Info<MainDom>("Acquired.");
return true;
} }
/// <summary> /// <summary>
/// Gets a value indicating whether the current domain is the main domain. /// Gets a value indicating whether the current domain is the main domain.
/// </summary> /// </summary>
public bool IsMainDom => _isMainDom; public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire());
// IRegisteredObject // IRegisteredObject
void IRegisteredObject.Stop(bool immediate) void IRegisteredObject.Stop(bool immediate)
{ {
try OnSignal("environment"); // will run once
// The web app is stopping, need to wind down
Dispose(true);
HostingEnvironment.UnregisterObject(this);
}
#region IDisposable Support
// This code added to correctly implement the disposable pattern.
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{ {
OnSignal("environment"); // will run once if (disposing)
} {
finally _signal?.Close();
{ _signal?.Dispose();
HostingEnvironment.UnregisterObject(this); }
disposedValue = true;
} }
} }
public void Dispose()
{
Dispose(true);
}
#endregion
} }
} }

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Text; using System.Text;
using Newtonsoft.Json; using Newtonsoft.Json;
using Umbraco.Core.Cache; using Umbraco.Core.Cache;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors;
@@ -42,7 +41,8 @@ namespace Umbraco.Core.Manifest
_cache = appCaches.RuntimeCache; _cache = appCaches.RuntimeCache;
_validators = validators ?? throw new ArgumentNullException(nameof(validators)); _validators = validators ?? throw new ArgumentNullException(nameof(validators));
_filters = filters ?? throw new ArgumentNullException(nameof(filters)); _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; Path = path;
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
} }
@@ -155,8 +155,8 @@ namespace Umbraco.Core.Manifest
/// </summary> /// </summary>
internal PackageManifest ParseManifest(string text) internal PackageManifest ParseManifest(string text)
{ {
if (string.IsNullOrWhiteSpace(text)) if (text == null) throw new ArgumentNullException(nameof(text));
throw new ArgumentNullOrEmptyException(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, var manifest = JsonConvert.DeserializeObject<PackageManifest>(text,
new DataEditorConverter(_logger), new DataEditorConverter(_logger),

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.Common;
using Umbraco.Core.Migrations.Expressions.Delete.Column; using Umbraco.Core.Migrations.Expressions.Delete.Column;
using Umbraco.Core.Migrations.Expressions.Delete.Constraint; using Umbraco.Core.Migrations.Expressions.Delete.Constraint;
@@ -39,8 +39,9 @@ namespace Umbraco.Core.Migrations.Expressions.Delete
/// <inheritdoc /> /// <inheritdoc />
public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true) public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true)
{ {
if (tableName.IsNullOrWhiteSpace()) if (tableName == null) throw new ArgumentNullException(nameof(tableName));
throw new ArgumentNullOrEmptyException(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 }; return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeleteLocal = local, DeleteForeign = foreign };
} }

View File

@@ -1,28 +1,49 @@
using System; using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Migrations namespace Umbraco.Core.Migrations
{ {
/// <summary> /// <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> /// </summary>
/// <remarks> /// <remarks>
/// <para>Migration expression such as Alter.Table(...).Do() *must* end with Do() else they are /// Migration expressions such as Alter.Table(...).Do() must end with Do(), else they are not executed.
/// not executed. When a non-executed expression is detected, an IncompleteMigrationExpressionException /// When a non-executed expression is detected, an IncompleteMigrationExpressionException is thrown.
/// is thrown.</para>
/// </remarks> /// </remarks>
/// <seealso cref="System.Exception" />
[Serializable]
public class IncompleteMigrationExpressionException : Exception public class IncompleteMigrationExpressionException : Exception
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException"/> class. /// Initializes a new instance of the <see cref="IncompleteMigrationExpressionException" /> class.
/// </summary> /// </summary>
public IncompleteMigrationExpressionException() public IncompleteMigrationExpressionException()
{ } { }
/// <summary> /// <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> /// </summary>
/// <param name="message">The message that describes the error.</param>
public IncompleteMigrationExpressionException(string message) public IncompleteMigrationExpressionException(string message)
: base(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.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Umbraco.Core.Configuration; using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.Upgrade; using Umbraco.Core.Migrations.Upgrade;
@@ -278,8 +277,10 @@ namespace Umbraco.Core.Migrations.Install
/// <param name="logger">A logger.</param> /// <param name="logger">A logger.</param>
private static void SaveConnectionString(string connectionString, string providerName, ILogger logger) private static void SaveConnectionString(string connectionString, string providerName, ILogger logger)
{ {
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString)); if (connectionString == null) throw new ArgumentNullException(nameof(connectionString));
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName)); 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 fileSource = "web.config";
var fileName = IOHelper.MapPath(SystemDirectories.Root +"/" + fileSource); var fileName = IOHelper.MapPath(SystemDirectories.Root +"/" + fileSource);

View File

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

View File

@@ -183,9 +183,11 @@ namespace Umbraco.Core.Migrations.Upgrade
To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}");
To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}");
// to 8.5.0... // to 8.6.0...
To<UpdateRelationTypeTable>("{4759A294-9860-46BC-99F9-B4C975CAE580}"); To<UpdateRelationTypeTable>("{4759A294-9860-46BC-99F9-B4C975CAE580}");
To<AddNewRelationTypes>("{0BC866BC-0665-487A-9913-0290BD0169AD}"); To<AddNewRelationTypes>("{0BC866BC-0665-487A-9913-0290BD0169AD}");
To<AddPropertyTypeValidationMessageColumns>("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}");
//FINAL //FINAL
} }

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.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
@@ -229,8 +227,8 @@ namespace Umbraco.Core.Models
private void ClearCultureInfo(string culture) private void ClearCultureInfo(string culture)
{ {
if (culture.IsNullOrWhiteSpace()) if (culture == null) throw new ArgumentNullException(nameof(culture));
throw new ArgumentNullOrEmptyException(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; if (_cultureInfos == null) return;
_cultureInfos.Remove(culture); _cultureInfos.Remove(culture);

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
@@ -18,7 +17,9 @@ namespace Umbraco.Core.Models
/// </summary> /// </summary>
public ContentCultureInfos(string culture) 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; Culture = culture;
} }

View File

@@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using Umbraco.Core.Collections; using Umbraco.Core.Collections;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
@@ -23,7 +21,9 @@ namespace Umbraco.Core.Models
/// </summary> /// </summary>
public void AddOrUpdate(string culture, string name, DateTime date) 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(); culture = culture.ToLowerInvariant();
if (TryGetValue(culture, out var item)) if (TryGetValue(culture, out var item))

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models 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) public static void SetPublishInfo(this IContent content, string culture, string name, DateTime date)
{ {
if (string.IsNullOrWhiteSpace(name)) if (name == null) throw new ArgumentNullException(nameof(name));
throw new ArgumentNullOrEmptyException(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()) if (culture == null) throw new ArgumentNullException(nameof(culture));
throw new ArgumentNullOrEmptyException(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); 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) public static void SetCultureInfo(this IContentBase content, string culture, string name, DateTime date)
{ {
if (name.IsNullOrWhiteSpace()) if (name == null) throw new ArgumentNullException(nameof(name));
throw new ArgumentNullOrEmptyException(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()) if (culture == null) throw new ArgumentNullException(nameof(culture));
throw new ArgumentNullOrEmptyException(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); content.CultureInfos.AddOrUpdate(culture, name, date);
} }
@@ -276,8 +275,8 @@ namespace Umbraco.Core.Models
/// <returns></returns> /// <returns></returns>
public static bool ClearPublishInfo(this IContent content, string culture) public static bool ClearPublishInfo(this IContent content, string culture)
{ {
if (culture.IsNullOrWhiteSpace()) if (culture == null) throw new ArgumentNullException(nameof(culture));
throw new ArgumentNullOrEmptyException(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); var removed = content.PublishCultureInfos.Remove(culture);
if (removed) if (removed)

View File

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

View File

@@ -1,10 +1,8 @@
using System; using System.Linq;
using System.Linq; using Umbraco.Core.Composing;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
@@ -18,29 +16,12 @@ namespace Umbraco.Core.Models
if (!media.Properties.TryGetValue(propertyAlias, out var property)) if (!media.Properties.TryGetValue(propertyAlias, out var property))
return string.Empty; return string.Empty;
// TODO: would need to be adjusted to variations, when media become variants if (Current.PropertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor)
if (!(property.GetValue() is string jsonString)) && editor is IDataEditorWithMediaPath dataEditor)
return string.Empty;
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField)
return jsonString;
if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.ImageCropper)
{ {
if (jsonString.DetectIsJson() == false) // TODO: would need to be adjusted to variations, when media become variants
return jsonString; var value = property.GetValue();
return dataEditor.GetMediaPath(value);
try
{
var json = JsonConvert.DeserializeObject<JObject>(jsonString);
if (json["src"] != null)
return json["src"].Value<string>();
}
catch (Exception ex)
{
logger.Error<ImageCropperValueConverter>(ex, "Could not parse the string '{JsonString}' to a json object", jsonString);
return string.Empty;
}
} }
// Without knowing what it is, just adding a string here might not be very nice // Without knowing what it is, just adding a string here might not be very nice

View File

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

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
@@ -9,7 +8,7 @@ namespace Umbraco.Core.Models
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
[DataContract(Name = "pagedCollection", Namespace = "")] [DataContract(Name = "pagedCollection", Namespace = "")]
public class PagedResult<T> public abstract class PagedResult
{ {
public PagedResult(long totalItems, long pageNumber, long pageSize) public PagedResult(long totalItems, long pageNumber, long pageSize)
{ {
@@ -39,9 +38,6 @@ namespace Umbraco.Core.Models
[DataMember(Name = "totalItems")] [DataMember(Name = "totalItems")]
public long TotalItems { get; private set; } public long TotalItems { get; private set; }
[DataMember(Name = "items")]
public IEnumerable<T> Items { get; set; }
/// <summary> /// <summary>
/// Calculates the skip size based on the paged parameters specified /// Calculates the skip size based on the paged parameters specified
/// </summary> /// </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 string _propertyEditorAlias;
private ValueStorageType _valueStorageType; private ValueStorageType _valueStorageType;
private bool _mandatory; private bool _mandatory;
private string _mandatoryMessage;
private int _sortOrder; private int _sortOrder;
private string _validationRegExp; private string _validationRegExp;
private string _validationRegExpMessage;
private ContentVariation _variations; private ContentVariation _variations;
/// <summary> /// <summary>
@@ -183,7 +185,7 @@ namespace Umbraco.Core.Models
} }
/// <summary> /// <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> /// </summary>
[DataMember] [DataMember]
public bool Mandatory public bool Mandatory
@@ -192,6 +194,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _mandatory, nameof(Mandatory)); 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> /// <summary>
/// Gets of sets the sort order of the property type. /// Gets of sets the sort order of the property type.
/// </summary> /// </summary>
@@ -212,6 +224,16 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _validationRegExp, nameof(ValidationRegExp)); 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> /// <summary>
/// Gets or sets the content variation of the property type. /// Gets or sets the content variation of the property type.
/// </summary> /// </summary>

View File

@@ -20,7 +20,9 @@ namespace Umbraco.Core.Models.PublishedContent
{ {
private ModelType(string contentTypeAlias) 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; ContentTypeAlias = contentTypeAlias;
Name = "{" + ContentTypeAlias + "}"; Name = "{" + ContentTypeAlias + "}";
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent namespace Umbraco.Core.Models.PublishedContent
{ {
@@ -13,7 +12,8 @@ namespace Umbraco.Core.Models.PublishedContent
/// </summary> /// </summary>
public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date) 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)); Culture = culture ?? throw new ArgumentNullException(nameof(culture));
Name = name; Name = name;

View File

@@ -1,5 +1,4 @@
using System; using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent namespace Umbraco.Core.Models.PublishedContent
{ {
@@ -19,7 +18,9 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="contentTypeAlias">The content type alias.</param> /// <param name="contentTypeAlias">The content type alias.</param>
public PublishedModelAttribute(string contentTypeAlias) 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; ContentTypeAlias = contentTypeAlias;
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models

View File

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

View File

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

View File

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

View File

@@ -1,38 +1,96 @@
using System; using System;
using System.Collections.Generic; using System.Runtime.Serialization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Core.Persistence namespace Umbraco.Core.Persistence
{ {
// TODO: Would be good to use this exception type anytime we cannot find an entity
/// <summary> /// <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> /// </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 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; } 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; 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() public override string ToString()
{ {
var result = base.ToString(); var result = base.ToString();

View File

@@ -1,17 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Persistence.Factories namespace Umbraco.Core.Persistence.Factories
{ {
internal class ContentBaseFactory internal class ContentBaseFactory
{ {
private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled);
/// <summary> /// <summary>
/// Builds an IContent item from a dto and content type. /// Builds an IContent item from a dto and content type.
/// </summary> /// </summary>
@@ -189,7 +187,7 @@ namespace Umbraco.Core.Persistence.Factories
/// <summary> /// <summary>
/// Builds a dto from an IMedia item. /// Builds a dto from an IMedia item.
/// </summary> /// </summary>
public static MediaDto BuildDto(IMedia entity) public static MediaDto BuildDto(PropertyEditorCollection propertyEditors, IMedia entity)
{ {
var contentDto = BuildContentDto(entity, Constants.ObjectTypes.Media); var contentDto = BuildContentDto(entity, Constants.ObjectTypes.Media);
@@ -197,7 +195,7 @@ namespace Umbraco.Core.Persistence.Factories
{ {
NodeId = entity.Id, NodeId = entity.Id,
ContentDto = contentDto, ContentDto = contentDto,
MediaVersionDto = BuildMediaVersionDto(entity, contentDto) MediaVersionDto = BuildMediaVersionDto(propertyEditors, entity, contentDto)
}; };
return dto; return dto;
@@ -291,12 +289,20 @@ namespace Umbraco.Core.Persistence.Factories
return dto; return dto;
} }
private static MediaVersionDto BuildMediaVersionDto(IMedia entity, ContentDto contentDto) private static MediaVersionDto BuildMediaVersionDto(PropertyEditorCollection propertyEditors, IMedia entity, ContentDto contentDto)
{ {
// try to get a path from the string being stored for media // try to get a path from the string being stored for media
// TODO: only considering umbracoFile // TODO: only considering umbracoFile
TryMatch(entity.GetValue<string>("umbracoFile"), out var path); string path = null;
if (entity.Properties.TryGetValue(Constants.Conventions.Media.File, out var property)
&& propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor)
&& editor is IDataEditorWithMediaPath dataEditor)
{
var value = property.GetValue();
path = dataEditor.GetMediaPath(value);
}
var dto = new MediaVersionDto var dto = new MediaVersionDto
{ {
@@ -308,22 +314,5 @@ namespace Umbraco.Core.Persistence.Factories
return dto; return dto;
} }
// TODO: this should NOT be here?!
// more dark magic ;-(
internal static bool TryMatch(string text, out string path)
{
// In v8 we should allow exposing this via the property editor in a much nicer way so that the property editor
// can tell us directly what any URL is for a given property if it contains an asset
path = null;
if (string.IsNullOrWhiteSpace(text)) return false;
var m = MediaPathPattern.Match(text);
if (!m.Success || m.Groups.Count != 2) return false;
path = m.Groups[1].Value;
return true;
}
} }
} }

View File

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

View File

@@ -4,59 +4,51 @@ using System.Runtime.Serialization;
namespace Umbraco.Core.Persistence.FaultHandling namespace Umbraco.Core.Persistence.FaultHandling
{ {
/// <summary> /// <summary>
/// The special type of exception that provides managed exit from a retry loop. The user code can use this /// 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.
/// exception to notify the retry policy that no further retry attempts are required.
/// </summary> /// </summary>
/// <seealso cref="System.Exception" />
[Serializable] [Serializable]
public sealed class RetryLimitExceededException : Exception public sealed class RetryLimitExceededException : Exception
{ {
/// <summary> /// <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> /// </summary>
public RetryLimitExceededException() public RetryLimitExceededException()
: this("RetryLimitExceeded") : base()
{ { }
}
/// <summary> /// <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> /// </summary>
/// <param name="message">The message that describes the error.</param> /// <param name="message">The message that describes the error.</param>
public RetryLimitExceededException(string message) public RetryLimitExceededException(string message)
: base(message) : base(message)
{ { }
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class with a reference to the inner 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.
/// that is the cause of this exception.
/// </summary> /// </summary>
/// <param name="innerException">The exception that is the cause of the current exception.</param> /// <param name="innerException">The exception that is the cause of the current exception.</param>
public RetryLimitExceededException(Exception innerException) public RetryLimitExceededException(Exception innerException)
: base(innerException != null ? innerException.Message : "RetryLimitExceeded", innerException) : base(null, innerException)
{ { }
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class. /// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class.
/// </summary> /// </summary>
/// <param name="message">The message that describes the error.</param> /// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception.</param> /// <param name="innerException">The exception that is the cause of the current exception.</param>
public RetryLimitExceededException(string message, Exception innerException) public RetryLimitExceededException(string message, Exception innerException)
: base(message, innerException) : base(message, innerException)
{ { }
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RetryLimitExceededException"/> class. /// Initializes a new instance of the <see cref="RetryLimitExceededException" /> class.
/// </summary> /// </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="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> /// <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>
private RetryLimitExceededException(SerializationInfo info, StreamingContext context) private RetryLimitExceededException(SerializationInfo info, StreamingContext context)
: base(info, 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.DataTypeId), nameof(PropertyTypeDto.DataTypeId));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description)); DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Description), nameof(PropertyTypeDto.Description));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.Mandatory), nameof(PropertyTypeDto.Mandatory)); 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.Name), nameof(PropertyTypeDto.Name));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder)); DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.SortOrder), nameof(PropertyTypeDto.SortOrder));
DefineMap<PropertyType, PropertyTypeDto>(nameof(PropertyType.ValidationRegExp), nameof(PropertyTypeDto.ValidationRegExp)); 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.PropertyEditorAlias), nameof(DataTypeDto.EditorAlias));
DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType)); DefineMap<PropertyType, DataTypeDto>(nameof(PropertyType.ValueStorageType), nameof(DataTypeDto.DbType));
} }

View File

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

View File

@@ -944,32 +944,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query) 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() protected override IEnumerable<string> GetDeleteClauses()
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override void PersistNewItem(IContent entity) protected override void PersistNewItem(IContent entity)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override void PersistUpdatedItem(IContent entity) 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) protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override string GetBaseWhereClause() 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 System.Linq;
using NPoco; using NPoco;
using Umbraco.Core.Cache; using Umbraco.Core.Cache;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Dtos;
@@ -129,6 +128,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override void PersistDeletedItem(EntityContainer entity) protected override void PersistDeletedItem(EntityContainer entity)
{ {
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(entity); EnsureContainerType(entity);
var nodeDto = Database.FirstOrDefault<NodeDto>(Sql().SelectAll() var nodeDto = Database.FirstOrDefault<NodeDto>(Sql().SelectAll()
@@ -162,9 +162,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override void PersistNewItem(EntityContainer entity) protected override void PersistNewItem(EntityContainer entity)
{ {
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(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(); entity.Name = entity.Name.Trim();
// guard against duplicates // guard against duplicates
@@ -184,7 +186,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.Where<NodeDto>(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType)); .Where<NodeDto>(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parentDto == null) 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; level = parentDto.Level;
path = parentDto.Path; path = parentDto.Path;
@@ -223,10 +225,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// //
protected override void PersistUpdatedItem(EntityContainer entity) protected override void PersistUpdatedItem(EntityContainer entity)
{ {
if (entity == null) throw new ArgumentNullException(nameof(entity));
EnsureContainerType(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(); entity.Name = entity.Name.Trim();
if (string.IsNullOrWhiteSpace(entity.Name)) throw new ArgumentNullOrEmptyException("entity.Name");
// find container to update // find container to update
var nodeDto = Database.FirstOrDefault<NodeDto>(Sql().SelectAll() 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)); .Where<NodeDto>(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parent == null) 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.Level = Convert.ToInt16(parent.Level + 1);
nodeDto.Path = parent.Path + "," + nodeDto.NodeId; nodeDto.Path = parent.Path + "," + nodeDto.NodeId;

View File

@@ -230,7 +230,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
entity.SanitizeEntityPropertiesForXmlStorage(); entity.SanitizeEntityPropertiesForXmlStorage();
// create the dto // create the dto
var dto = ContentBaseFactory.BuildDto(entity); var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity);
// derive path and level from parent // derive path and level from parent
var parent = GetParentNodeDto(entity.ParentId); var parent = GetParentNodeDto(entity.ParentId);
@@ -321,7 +321,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
} }
// create the dto // create the dto
var dto = ContentBaseFactory.BuildDto(entity); var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity);
// update the node dto // update the node dto
var nodeDto = dto.ContentDto.NodeDto; var nodeDto = dto.ContentDto.NodeDto;
@@ -431,32 +431,32 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query) 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() protected override IEnumerable<string> GetDeleteClauses()
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override void PersistNewItem(IMedia entity) protected override void PersistNewItem(IMedia entity)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override void PersistUpdatedItem(IMedia entity) 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) protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override string GetBaseWhereClause() protected override string GetBaseWhereClause()
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
} }
@@ -548,6 +548,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
media.ResetDirtyProperties(false); media.ResetDirtyProperties(false);
return media; return media;
} }
} }
} }

View File

@@ -225,8 +225,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (builtinProperties.ContainsKey(propertyType.Alias)) 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 //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; var propDefinition = builtinProperties[propertyType.Alias];
propertyType.DataTypeKey = default; 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) 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) 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) 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) protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override string GetBaseWhereClause() protected override string GetBaseWhereClause()
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override IEnumerable<string> GetDeleteClauses() protected override IEnumerable<string> GetDeleteClauses()
@@ -280,11 +280,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return new List<string>(); 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) protected override void PersistDeletedItem(ContentPermissionSet entity)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
#endregion #endregion

View File

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

View File

@@ -286,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return list; 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) protected override void PersistNewItem(IUserGroup entity)
{ {
@@ -370,35 +370,35 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override UserGroupWithUsers PerformGet(int id) 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) 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) 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) protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override string GetBaseWhereClause() protected override string GetBaseWhereClause()
{ {
throw new WontImplementException(); throw new InvalidOperationException("This method won't be implemented.");
} }
protected override IEnumerable<string> GetDeleteClauses() 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 #endregion

View File

@@ -4,7 +4,6 @@ using System.Data.Common;
using System.Threading; using System.Threading;
using NPoco; using NPoco;
using NPoco.FluentMappings; using NPoco.FluentMappings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.FaultHandling; using Umbraco.Core.Persistence.FaultHandling;
using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Mappers;
@@ -61,8 +60,8 @@ namespace Umbraco.Core.Persistence
/// <remarks>Used by the other ctor and in tests.</remarks> /// <remarks>Used by the other ctor and in tests.</remarks>
public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy<IMapperCollection> mappers) public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy<IMapperCollection> mappers)
{ {
if (string.IsNullOrWhiteSpace(connectionStringName)) if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName));
throw new ArgumentNullOrEmptyException(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)); _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));

View File

@@ -1,5 +1,4 @@
using System; using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.PropertyEditors 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> /// <param name="view">The view to use to render the field editor.</param>
public ConfigurationFieldAttribute(string key, string name, string view) 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; Key = key;
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name; Name = name;
if (string.IsNullOrWhiteSpace(view)) throw new ArgumentNullOrEmptyException(nameof(view));
View = view; View = view;
} }
@@ -47,10 +48,12 @@ namespace Umbraco.Core.PropertyEditors
/// from the name of the property marked with this attribute.</remarks> /// from the name of the property marked with this attribute.</remarks>
public ConfigurationFieldAttribute(string name, string view) public ConfigurationFieldAttribute(string name, string view)
{ {
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
Name = 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 = view;
} }

View File

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

View File

@@ -0,0 +1,19 @@
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Must be implemented by property editors that store media and return media paths
/// </summary>
/// <remarks>
/// Currently there are only 2x core editors that do this: upload and image cropper.
/// It would be possible for developers to know implement their own media property editors whereas previously this was not possible.
/// </remarks>
public interface IDataEditorWithMediaPath
{
/// <summary>
/// Returns the media path for the value stored for a property
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
string GetMediaPath(object value);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core namespace Umbraco.Core
{ {
@@ -29,10 +28,14 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam> /// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam> /// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param> /// <param name="fieldName">The name of the field.</param>
/// <returns>A field getter function.</returns> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception> /// A field getter function.
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception> /// <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) public static Func<TDeclaring, TValue> EmitFieldGetter<TDeclaring, TValue>(string fieldName)
{ {
var field = GetField<TDeclaring, TValue>(fieldName); var field = GetField<TDeclaring, TValue>(fieldName);
@@ -45,10 +48,14 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam> /// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam> /// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param> /// <param name="fieldName">The name of the field.</param>
/// <returns>A field setter action.</returns> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception> /// A field setter action.
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception> /// <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) public static Action<TDeclaring, TValue> EmitFieldSetter<TDeclaring, TValue>(string fieldName)
{ {
var field = GetField<TDeclaring, TValue>(fieldName); var field = GetField<TDeclaring, TValue>(fieldName);
@@ -61,19 +68,36 @@ namespace Umbraco.Core
/// <typeparam name="TDeclaring">The declaring type.</typeparam> /// <typeparam name="TDeclaring">The declaring type.</typeparam>
/// <typeparam name="TValue">The field type.</typeparam> /// <typeparam name="TValue">The field type.</typeparam>
/// <param name="fieldName">The name of the field.</param> /// <param name="fieldName">The name of the field.</param>
/// <returns>A field getter and setter functions.</returns> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="fieldName"/> is null or empty.</exception> /// A field getter and setter functions.
/// <exception cref="InvalidOperationException">Occurs when the field does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the field.</exception> /// <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) public static (Func<TDeclaring, TValue>, Action<TDeclaring, TValue>) EmitFieldGetterAndSetter<TDeclaring, TValue>(string fieldName)
{ {
var field = GetField<TDeclaring, TValue>(fieldName); var field = GetField<TDeclaring, TValue>(fieldName);
return (EmitFieldGetter<TDeclaring, TValue>(field), EmitFieldSetter<TDeclaring, TValue>(field)); 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) 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 // get the field
var field = typeof(TDeclaring).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 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> /// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param> /// <param name="propertyName">The name of the property.</param>
/// <param name="mustExist">A value indicating whether the property and its getter must exist.</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> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception> /// A property getter function. If <paramref name="mustExist" /> is <c>false</c>, returns null when the property or its getter does not exist.
/// <exception cref="InvalidOperationException">Occurs when the property or its getter does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception> /// <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) 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); 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> /// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param> /// <param name="propertyName">The name of the property.</param>
/// <param name="mustExist">A value indicating whether the property and its setter must exist.</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> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception> /// A property setter function. If <paramref name="mustExist" /> is <c>false</c>, returns null when the property or its setter does not exist.
/// <exception cref="InvalidOperationException">Occurs when the property or its setter does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception> /// <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) 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); 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> /// <typeparam name="TValue">The property type.</typeparam>
/// <param name="propertyName">The name of the property.</param> /// <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> /// <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> /// <returns>
/// <exception cref="ArgumentNullOrEmptyException">Occurs when <paramref name="propertyName"/> is null or empty.</exception> /// 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.
/// <exception cref="InvalidOperationException">Occurs when the property or its getter or setter does not exist.</exception> /// </returns>
/// <exception cref="ArgumentException">Occurs when <typeparamref name="TValue"/> does not match the type of the property.</exception> /// <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) 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); 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> /// <typeparam name="TLambda">A lambda representing the method.</typeparam>
/// <param name="methodName">The name of the method.</param> /// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</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> /// <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> /// </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) public static TLambda EmitMethod<TDeclaring, TLambda>(string methodName, bool mustExist = true)
{ {
return EmitMethod<TLambda>(typeof(TDeclaring), methodName, mustExist); return EmitMethod<TLambda>(typeof(TDeclaring), methodName, mustExist);
@@ -421,16 +464,21 @@ namespace Umbraco.Core
/// <param name="declaring">The declaring type.</param> /// <param name="declaring">The declaring type.</param>
/// <param name="methodName">The name of the method.</param> /// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</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> /// <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> /// </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) 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); 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> /// <typeparam name="TLambda">A lambda representing the method.</typeparam>
/// <param name="methodName">The name of the method.</param> /// <param name="methodName">The name of the method.</param>
/// <param name="mustExist">A value indicating whether the constructor must exist.</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> /// <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> /// </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) public static TLambda EmitMethod<TLambda>(string methodName, bool mustExist = true)
{ {
if (string.IsNullOrWhiteSpace(methodName)) if (methodName == null) throw new ArgumentNullException(nameof(methodName));
throw new ArgumentNullOrEmptyException(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 // validate lambda type
var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda<TLambda>(false, out var isFunction); var (lambdaDeclaring, lambdaParameters, lambdaReturned) = AnalyzeLambda<TLambda>(false, out var isFunction);

View File

@@ -27,6 +27,17 @@ namespace Umbraco.Core.Runtime
private IFactory _factory; private IFactory _factory;
private RuntimeState _state; private RuntimeState _state;
[Obsolete("Use the ctor with all parameters instead")]
public CoreRuntime()
{
}
public CoreRuntime(ILogger logger, IMainDom mainDom)
{
MainDom = mainDom;
Logger = logger;
}
/// <summary> /// <summary>
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
@@ -45,14 +56,22 @@ namespace Umbraco.Core.Runtime
/// <inheritdoc /> /// <inheritdoc />
public IRuntimeState State => _state; public IRuntimeState State => _state;
public IMainDom MainDom { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
public virtual IFactory Boot(IRegister register) public virtual IFactory Boot(IRegister register)
{ {
// create and register the essential services // create and register the essential services
// ie the bare minimum required to boot // ie the bare minimum required to boot
#pragma warning disable CS0618 // Type or member is obsolete
// loggers // loggers
var logger = Logger = GetLogger(); // TODO: Removes this in netcore, this is purely just backwards compat ugliness
var logger = GetLogger();
if (logger != Logger)
Logger = logger;
#pragma warning restore CS0618 // Type or member is obsolete
var profiler = Profiler = GetProfiler(); var profiler = Profiler = GetProfiler();
var profilingLogger = ProfilingLogger = new ProfilingLogger(logger, profiler); var profilingLogger = ProfilingLogger = new ProfilingLogger(logger, profiler);
@@ -125,12 +144,16 @@ namespace Umbraco.Core.Runtime
Level = RuntimeLevel.Boot Level = RuntimeLevel.Boot
}; };
// main dom // TODO: remove this in netcore, this is purely backwards compat hacks with the empty ctor
var mainDom = new MainDom(Logger); if (MainDom == null)
{
MainDom = new MainDom(Logger);
}
// create the composition // create the composition
composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs); composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs);
composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state); composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, MainDom, appCaches, databaseFactory, typeLoader, _state);
// run handlers // run handlers
RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory); RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory);
@@ -140,15 +163,21 @@ namespace Umbraco.Core.Runtime
Compose(composition); Compose(composition);
// acquire the main domain - if this fails then anything that should be registered with MainDom will not operate // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate
AcquireMainDom(mainDom); AcquireMainDom(MainDom);
// determine our runtime level // determine our runtime level
DetermineRuntimeLevel(databaseFactory, ProfilingLogger); DetermineRuntimeLevel(databaseFactory, ProfilingLogger);
// get composers, and compose // get composers, and compose
var composerTypes = ResolveComposerTypes(typeLoader); var composerTypes = ResolveComposerTypes(typeLoader);
composition.WithCollectionBuilder<ComponentCollectionBuilder>();
var composers = new Composers(composition, composerTypes, ProfilingLogger); IEnumerable<Attribute> enableDisableAttributes;
using (ProfilingLogger.DebugDuration<CoreRuntime>("Scanning enable/disable composer attributes"))
{
enableDisableAttributes = typeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute));
}
var composers = new Composers(composition, composerTypes, enableDisableAttributes, ProfilingLogger);
composers.Compose(); composers.Compose();
// create the factory // create the factory
@@ -157,6 +186,8 @@ namespace Umbraco.Core.Runtime
// create & initialize the components // create & initialize the components
_components = _factory.GetInstance<ComponentCollection>(); _components = _factory.GetInstance<ComponentCollection>();
_components.Initialize(); _components.Initialize();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -218,13 +249,13 @@ namespace Umbraco.Core.Runtime
IOHelper.SetRootDirectory(path); IOHelper.SetRootDirectory(path);
} }
private bool AcquireMainDom(MainDom mainDom) private bool AcquireMainDom(IMainDom mainDom)
{ {
using (var timer = ProfilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired.")) using (var timer = ProfilingLogger.DebugDuration<CoreRuntime>("Acquiring MainDom.", "Acquired."))
{ {
try try
{ {
return mainDom.Acquire(); return mainDom.IsMainDom;
} }
catch catch
{ {
@@ -301,11 +332,9 @@ namespace Umbraco.Core.Runtime
protected virtual IEnumerable<Type> GetComposerTypes(TypeLoader typeLoader) protected virtual IEnumerable<Type> GetComposerTypes(TypeLoader typeLoader)
=> typeLoader.GetTypes<IComposer>(); => typeLoader.GetTypes<IComposer>();
/// <summary> [Obsolete("Don't use this method, the logger should be injected into the " + nameof(CoreRuntime))]
/// Gets a logger.
/// </summary>
protected virtual ILogger GetLogger() protected virtual ILogger GetLogger()
=> SerilogLogger.CreateWithDefaultConfiguration(); => Logger ?? SerilogLogger.CreateWithDefaultConfiguration(); // TODO: Remove this in netcore, this purely just backwards compat ugliness
/// <summary> /// <summary>
/// Gets a profiler. /// Gets a profiler.

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Web; using System.Web;
using Semver; using Semver;
using Umbraco.Core.Collections;
using Umbraco.Core.Configuration; using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions; using Umbraco.Core.Exceptions;
@@ -22,7 +22,7 @@ namespace Umbraco.Core
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IUmbracoSettingsSection _settings; private readonly IUmbracoSettingsSection _settings;
private readonly IGlobalSettings _globalSettings; private readonly IGlobalSettings _globalSettings;
private readonly HashSet<string> _applicationUrls = new HashSet<string>(); private readonly ConcurrentHashSet<string> _applicationUrls = new ConcurrentHashSet<string>();
private readonly Lazy<IMainDom> _mainDom; private readonly Lazy<IMainDom> _mainDom;
private readonly Lazy<IServerRegistrar> _serverRegistrar; private readonly Lazy<IServerRegistrar> _serverRegistrar;

View File

@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Web.Security; using System.Web.Security;
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity;
using Umbraco.Core.Configuration; using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Mapping; using Umbraco.Core.Mapping;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Identity;
@@ -217,7 +216,8 @@ namespace Umbraco.Core.Security
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user)); 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; user.PasswordHash = passwordHash;
@@ -329,7 +329,7 @@ namespace Umbraco.Core.Security
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user)); 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 logins = user.Logins;
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id); var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id);
@@ -348,7 +348,7 @@ namespace Umbraco.Core.Security
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user)); 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 provider = login.LoginProvider;
var key = login.ProviderKey; var key = login.ProviderKey;
@@ -379,7 +379,7 @@ namespace Umbraco.Core.Security
public Task<BackOfficeIdentityUser> FindAsync(UserLoginInfo login) public Task<BackOfficeIdentityUser> FindAsync(UserLoginInfo login)
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (login == null) throw new ArgumentNullException("login"); if (login == null) throw new ArgumentNullException(nameof(login));
//get all logins associated with the login id //get all logins associated with the login id
var result = _externalLoginService.Find(login).ToArray(); var result = _externalLoginService.Find(login).ToArray();
@@ -413,7 +413,8 @@ namespace Umbraco.Core.Security
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user)); 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); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);
@@ -434,7 +435,8 @@ namespace Umbraco.Core.Security
{ {
ThrowIfDisposed(); ThrowIfDisposed();
if (user == null) throw new ArgumentNullException(nameof(user)); 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); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);

View File

@@ -2885,7 +2885,8 @@ namespace Umbraco.Core.Services.Implement
private IContentType GetContentType(IScope scope, string contentTypeAlias) 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); scope.ReadLock(Constants.Locks.ContentTypes);
@@ -2900,7 +2901,8 @@ namespace Umbraco.Core.Services.Implement
private IContentType GetContentType(string contentTypeAlias) 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)) using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{ {
@@ -3083,7 +3085,7 @@ namespace Umbraco.Core.Services.Implement
var version = GetVersion(versionId); var version = GetVersion(versionId);
//Good ole null checks //Good ole null checks
if (content == null || version == null) if (content == null || version == null || content.Trashed)
{ {
return new OperationResult(OperationResultType.FailedCannot, evtMsgs); return new OperationResult(OperationResultType.FailedCannot, evtMsgs);
} }

View File

@@ -595,10 +595,9 @@ namespace Umbraco.Core.Services.Implement
public TItem Copy(TItem original, string alias, string name, TItem parent) public TItem Copy(TItem original, string alias, string name, TItem parent)
{ {
if (original == null) throw new ArgumentNullException(nameof(original)); if (original == null) throw new ArgumentNullException(nameof(original));
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));
if (parent != null && parent.HasIdentity == false) if (parent != null && parent.HasIdentity == false) throw new InvalidOperationException("Parent must have an identity.");
throw new InvalidOperationException("Parent must have an identity.");
// this is illegal // this is illegal
//var originalb = (ContentTypeCompositionBase)original; //var originalb = (ContentTypeCompositionBase)original;

View File

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

View File

@@ -5,7 +5,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Umbraco.Core.Events; using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Models; using Umbraco.Core.Models;
@@ -1340,7 +1339,8 @@ namespace Umbraco.Core.Services.Implement
private IMediaType GetMediaType(string mediaTypeAlias) private IMediaType GetMediaType(string mediaTypeAlias)
{ {
if (string.IsNullOrWhiteSpace(mediaTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(mediaTypeAlias)); if (mediaTypeAlias == null) throw new ArgumentNullException(nameof(mediaTypeAlias));
if (string.IsNullOrWhiteSpace(mediaTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(mediaTypeAlias));
using (var scope = ScopeProvider.CreateScope()) using (var scope = ScopeProvider.CreateScope())
{ {
@@ -1350,7 +1350,7 @@ namespace Umbraco.Core.Services.Implement
var mediaType = _mediaTypeRepository.Get(query).FirstOrDefault(); var mediaType = _mediaTypeRepository.Get(query).FirstOrDefault();
if (mediaType == null) if (mediaType == null)
throw new Exception($"No MediaType matching the passed in Alias: '{mediaTypeAlias}' was found"); // causes rollback // causes rollback throw new InvalidOperationException($"No media type matched the specified alias '{mediaTypeAlias}'.");
scope.Complete(); scope.Complete();
return mediaType; return mediaType;

View File

@@ -1344,7 +1344,8 @@ namespace Umbraco.Core.Services.Implement
private IMemberType GetMemberType(IScope scope, string memberTypeAlias) private IMemberType GetMemberType(IScope scope, string memberTypeAlias)
{ {
if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(memberTypeAlias)); if (memberTypeAlias == null) throw new ArgumentNullException(nameof(memberTypeAlias));
if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(memberTypeAlias));
scope.ReadLock(Constants.Locks.MemberTypes); scope.ReadLock(Constants.Locks.MemberTypes);
@@ -1358,7 +1359,8 @@ namespace Umbraco.Core.Services.Implement
private IMemberType GetMemberType(string memberTypeAlias) private IMemberType GetMemberType(string memberTypeAlias)
{ {
if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(memberTypeAlias)); if (memberTypeAlias == null) throw new ArgumentNullException(nameof(memberTypeAlias));
if (string.IsNullOrWhiteSpace(memberTypeAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(memberTypeAlias));
using (var scope = ScopeProvider.CreateScope(autoComplete: true)) using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{ {

View File

@@ -57,7 +57,7 @@ namespace Umbraco.Core.Services.Implement
} }
catch (HttpRequestException ex) catch (HttpRequestException ex)
{ {
throw new ConnectionException("An error occurring downloading the package from " + url, ex); throw new HttpRequestException("An error occurring downloading the package from " + url, ex);
} }
//successful //successful

View File

@@ -1,15 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common; using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using Umbraco.Core.Configuration; using Umbraco.Core.Configuration;
using Umbraco.Core.Events; using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -17,7 +13,6 @@ using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Scoping; using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
namespace Umbraco.Core.Services.Implement namespace Umbraco.Core.Services.Implement
{ {
@@ -107,7 +102,8 @@ namespace Umbraco.Core.Services.Implement
/// <returns><see cref="IUser"/></returns> /// <returns><see cref="IUser"/></returns>
private IUser CreateUserWithIdentity(string username, string email, string passwordValue, bool isApproved = true) private IUser CreateUserWithIdentity(string username, string email, string passwordValue, bool isApproved = true)
{ {
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullOrEmptyException(nameof(username)); 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));
// TODO: PUT lock here!! // TODO: PUT lock here!!

View File

@@ -10,9 +10,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web.Security; using System.Web.Security;
using Newtonsoft.Json; using Newtonsoft.Json;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing; using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Strings; using Umbraco.Core.Strings;
@@ -1091,7 +1089,8 @@ namespace Umbraco.Core
/// <returns>The safe url segment.</returns> /// <returns>The safe url segment.</returns>
public static string ToUrlSegment(this string text) public static string ToUrlSegment(this 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));
return Current.ShortStringHelper.CleanStringForUrlSegment(text); return Current.ShortStringHelper.CleanStringForUrlSegment(text);
} }
@@ -1104,7 +1103,8 @@ namespace Umbraco.Core
/// <returns>The safe url segment.</returns> /// <returns>The safe url segment.</returns>
public static string ToUrlSegment(this string text, string culture) public static string ToUrlSegment(this string text, string culture)
{ {
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));
return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture);
} }

View File

@@ -21,18 +21,18 @@ namespace Umbraco.Core
// been closed, the Semaphore system object is destroyed - so in any case // been closed, the Semaphore system object is destroyed - so in any case
// an iisreset should clean up everything // an iisreset should clean up everything
// //
internal class AsyncLock internal class SystemLock
{ {
private readonly SemaphoreSlim _semaphore; private readonly SemaphoreSlim _semaphore;
private readonly Semaphore _semaphore2; private readonly Semaphore _semaphore2;
private readonly IDisposable _releaser; private readonly IDisposable _releaser;
private readonly Task<IDisposable> _releaserTask; private readonly Task<IDisposable> _releaserTask;
public AsyncLock() public SystemLock()
: this (null) : this(null)
{ } { }
public AsyncLock(string name) public SystemLock(string name)
{ {
// WaitOne() waits until count > 0 then decrements count // WaitOne() waits until count > 0 then decrements count
// Release() increments count // Release() increments count
@@ -67,35 +67,6 @@ namespace Umbraco.Core
: new NamedSemaphoreReleaser(_semaphore2); : new NamedSemaphoreReleaser(_semaphore2);
} }
//NOTE: We don't use the "Async" part of this lock at all
//TODO: Remove this and rename this class something like SystemWideLock, then we can re-instate this logic if we ever need an Async lock again
//public Task<IDisposable> LockAsync()
//{
// var wait = _semaphore != null
// ? _semaphore.WaitAsync()
// : _semaphore2.WaitOneAsync();
// return wait.IsCompleted
// ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
// : wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
// this, CancellationToken.None,
// TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//}
//public Task<IDisposable> LockAsync(int millisecondsTimeout)
//{
// var wait = _semaphore != null
// ? _semaphore.WaitAsync(millisecondsTimeout)
// : _semaphore2.WaitOneAsync(millisecondsTimeout);
// return wait.IsCompleted
// ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
// : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()),
// this, CancellationToken.None,
// TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//}
public IDisposable Lock() public IDisposable Lock()
{ {
if (_semaphore != null) if (_semaphore != null)
@@ -121,14 +92,18 @@ namespace Umbraco.Core
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
{ {
private readonly Semaphore _semaphore; private readonly Semaphore _semaphore;
private GCHandle _handle;
internal NamedSemaphoreReleaser(Semaphore semaphore) internal NamedSemaphoreReleaser(Semaphore semaphore)
{ {
_semaphore = semaphore; _semaphore = semaphore;
_handle = GCHandle.Alloc(_semaphore);
} }
#region IDisposable Support
// This code added to correctly implement the disposable pattern.
private bool disposedValue = false; // To detect redundant calls
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
@@ -137,10 +112,22 @@ namespace Umbraco.Core
private void Dispose(bool disposing) private void Dispose(bool disposing)
{ {
// critical if (!disposedValue)
_handle.Free(); {
_semaphore.Release(); try
_semaphore.Dispose(); {
_semaphore.Release();
}
finally
{
try
{
_semaphore.Dispose();
}
catch { }
}
disposedValue = true;
}
} }
// we WANT to release the semaphore because it's a system object, ie a critical // we WANT to release the semaphore because it's a system object, ie a critical
@@ -171,6 +158,9 @@ namespace Umbraco.Core
// we do NOT want the finalizer to throw - never ever // we do NOT want the finalizer to throw - never ever
} }
} }
#endregion
} }
private class SemaphoreSlimReleaser : IDisposable private class SemaphoreSlimReleaser : IDisposable

View File

@@ -128,7 +128,7 @@
</Compile> </Compile>
--> -->
<Compile Include="AssemblyExtensions.cs" /> <Compile Include="AssemblyExtensions.cs" />
<Compile Include="AsyncLock.cs" /> <Compile Include="SystemLock.cs" />
<Compile Include="Attempt.cs" /> <Compile Include="Attempt.cs" />
<Compile Include="AttemptOfTResult.cs" /> <Compile Include="AttemptOfTResult.cs" />
<Compile Include="AttemptOfTResultTStatus.cs" /> <Compile Include="AttemptOfTResultTStatus.cs" />
@@ -263,6 +263,7 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameMediaVersionTable.cs" /> <Compile Include="Migrations\Upgrade\V_8_0_0\RenameMediaVersionTable.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\ValueListPreValueMigrator.cs" /> <Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\ValueListPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\UmbracoSliderPreValueMigrator.cs" /> <Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\UmbracoSliderPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_6_0\AddPropertyTypeValidationMessageColumns.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\FixContentNuCascade.cs" /> <Compile Include="Migrations\Upgrade\V_8_1_0\FixContentNuCascade.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\RenameUserLoginDtoDateIndex.cs" /> <Compile Include="Migrations\Upgrade\V_8_1_0\RenameUserLoginDtoDateIndex.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_1\ChangeNuCacheJsonFormat.cs" /> <Compile Include="Migrations\Upgrade\V_8_0_1\ChangeNuCacheJsonFormat.cs" />
@@ -276,6 +277,7 @@
<Compile Include="Models\Entities\IMediaEntitySlim.cs" /> <Compile Include="Models\Entities\IMediaEntitySlim.cs" />
<Compile Include="Models\Entities\IMemberEntitySlim.cs" /> <Compile Include="Models\Entities\IMemberEntitySlim.cs" />
<Compile Include="Models\Entities\MediaEntitySlim.cs" /> <Compile Include="Models\Entities\MediaEntitySlim.cs" />
<Compile Include="Models\PagedResult.cs" />
<Compile Include="Models\Entities\MemberEntitySlim.cs" /> <Compile Include="Models\Entities\MemberEntitySlim.cs" />
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" /> <Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentType.cs" /> <Compile Include="Models\PublishedContent\IPublishedContentType.cs" />
@@ -285,6 +287,7 @@
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionBuilder.cs" /> <Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionBuilder.cs" />
<Compile Include="PropertyEditors\IDataValueReference.cs" /> <Compile Include="PropertyEditors\IDataValueReference.cs" />
<Compile Include="PropertyEditors\IDataValueReferenceFactory.cs" /> <Compile Include="PropertyEditors\IDataValueReferenceFactory.cs" />
<Compile Include="PropertyEditors\IDataEditorWithMediaPath.cs" />
<Compile Include="PropertyEditors\IIgnoreUserStartNodesConfig.cs" /> <Compile Include="PropertyEditors\IIgnoreUserStartNodesConfig.cs" />
<Compile Include="PublishedContentExtensions.cs" /> <Compile Include="PublishedContentExtensions.cs" />
<Compile Include="Models\PublishedContent\UrlMode.cs" /> <Compile Include="Models\PublishedContent\UrlMode.cs" />
@@ -686,7 +689,6 @@
<Compile Include="Exceptions\ArgumentNullOrEmptyException.cs" /> <Compile Include="Exceptions\ArgumentNullOrEmptyException.cs" />
<Compile Include="Exceptions\AuthorizationException.cs" /> <Compile Include="Exceptions\AuthorizationException.cs" />
<Compile Include="Exceptions\BootFailedException.cs" /> <Compile Include="Exceptions\BootFailedException.cs" />
<Compile Include="Exceptions\ConnectionException.cs" />
<Compile Include="Exceptions\DataOperationException.cs" /> <Compile Include="Exceptions\DataOperationException.cs" />
<Compile Include="Exceptions\InvalidCompositionException.cs" /> <Compile Include="Exceptions\InvalidCompositionException.cs" />
<Compile Include="Composing\LightInject\LightInjectException.cs" /> <Compile Include="Composing\LightInject\LightInjectException.cs" />
@@ -854,7 +856,7 @@
<Compile Include="Models\Packaging\InstallationSummary.cs" /> <Compile Include="Models\Packaging\InstallationSummary.cs" />
<Compile Include="Models\Packaging\PackageAction.cs" /> <Compile Include="Models\Packaging\PackageAction.cs" />
<Compile Include="Models\Packaging\PreInstallWarnings.cs" /> <Compile Include="Models\Packaging\PreInstallWarnings.cs" />
<Compile Include="Models\PagedResult.cs" /> <Compile Include="Models\PagedResultOfT.cs" />
<Compile Include="Models\PartialView.cs" /> <Compile Include="Models\PartialView.cs" />
<Compile Include="Models\PartialViewType.cs" /> <Compile Include="Models\PartialViewType.cs" />
<Compile Include="Models\Property.cs" /> <Compile Include="Models\Property.cs" />
@@ -1071,7 +1073,6 @@
<Compile Include="Persistence\Mappers\UserGroupMapper.cs" /> <Compile Include="Persistence\Mappers\UserGroupMapper.cs" />
<Compile Include="Persistence\Mappers\UserMapper.cs" /> <Compile Include="Persistence\Mappers\UserMapper.cs" />
<Compile Include="Persistence\Mappers\UserSectionMapper.cs" /> <Compile Include="Persistence\Mappers\UserSectionMapper.cs" />
<Compile Include="Migrations\DataLossException.cs" />
<Compile Include="Migrations\IMigration.cs" /> <Compile Include="Migrations\IMigration.cs" />
<Compile Include="Migrations\IMigrationContext.cs" /> <Compile Include="Migrations\IMigrationContext.cs" />
<Compile Include="Migrations\IMigrationExpression.cs" /> <Compile Include="Migrations\IMigrationExpression.cs" />

View File

@@ -1,9 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Xml namespace Umbraco.Core.Xml
{ {
@@ -37,7 +34,8 @@ namespace Umbraco.Core.Xml
// allowed 'inline', not just at the beginning... whether or not we want to support that is up // allowed 'inline', not just at the beginning... whether or not we want to support that is up
// for discussion. // for discussion.
if (string.IsNullOrWhiteSpace(xpathExpression)) throw new ArgumentNullOrEmptyException(nameof(xpathExpression)); if (xpathExpression == null) throw new ArgumentNullException(nameof(xpathExpression));
if (string.IsNullOrWhiteSpace(xpathExpression)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(xpathExpression));
if (getPath == null) throw new ArgumentNullException(nameof(getPath)); if (getPath == null) throw new ArgumentNullException(nameof(getPath));
if (publishedContentExists == null) throw new ArgumentNullException(nameof(publishedContentExists)); if (publishedContentExists == null) throw new ArgumentNullException(nameof(publishedContentExists));

View File

@@ -4,9 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath; using System.Xml.XPath;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO; using Umbraco.Core.IO;
namespace Umbraco.Core.Xml namespace Umbraco.Core.Xml
@@ -25,9 +23,10 @@ namespace Umbraco.Core.Xml
/// <param name="value"></param> /// <param name="value"></param>
public static void SetAttribute(XmlDocument xml, XmlNode n, string name, string value) public static void SetAttribute(XmlDocument xml, XmlNode n, string name, string value)
{ {
if (xml == null) throw new ArgumentNullException("xml"); if (xml == null) throw new ArgumentNullException(nameof(xml));
if (n == null) throw new ArgumentNullException("n"); if (n == null) throw new ArgumentNullException(nameof(n));
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 (n.Attributes == null) if (n.Attributes == null)
{ {
@@ -229,8 +228,9 @@ namespace Umbraco.Core.Xml
/// <returns>a XmlAttribute</returns> /// <returns>a XmlAttribute</returns>
public static XmlAttribute AddAttribute(XmlDocument xd, string name, string value) public static XmlAttribute AddAttribute(XmlDocument xd, string name, string value)
{ {
if (xd == null) throw new ArgumentNullException("xd"); if (xd == null) throw new ArgumentNullException(nameof(xd));
if (string.IsNullOrEmpty(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));
var temp = xd.CreateAttribute(name); var temp = xd.CreateAttribute(name);
temp.Value = value; temp.Value = value;
@@ -246,8 +246,9 @@ namespace Umbraco.Core.Xml
/// <returns>a XmlNode</returns> /// <returns>a XmlNode</returns>
public static XmlNode AddTextNode(XmlDocument xd, string name, string value) public static XmlNode AddTextNode(XmlDocument xd, string name, string value)
{ {
if (xd == null) throw new ArgumentNullException("xd"); if (xd == null) throw new ArgumentNullException(nameof(xd));
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));
var temp = xd.CreateNode(XmlNodeType.Element, name, ""); var temp = xd.CreateNode(XmlNodeType.Element, name, "");
temp.AppendChild(xd.CreateTextNode(value)); temp.AppendChild(xd.CreateTextNode(value));
@@ -264,9 +265,10 @@ namespace Umbraco.Core.Xml
/// <returns>a XmlNode</returns> /// <returns>a XmlNode</returns>
public static XmlNode SetTextNode(XmlDocument xd, XmlNode parent, string name, string value) public static XmlNode SetTextNode(XmlDocument xd, XmlNode parent, string name, string value)
{ {
if (xd == null) throw new ArgumentNullException("xd"); if (xd == null) throw new ArgumentNullException(nameof(xd));
if (parent == null) throw new ArgumentNullException("parent"); if (parent == null) throw new ArgumentNullException(nameof(parent));
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));
var child = parent.SelectSingleNode(name); var child = parent.SelectSingleNode(name);
if (child != null) if (child != null)
@@ -289,7 +291,8 @@ namespace Umbraco.Core.Xml
{ {
if (xd == null) throw new ArgumentNullException(nameof(xd)); if (xd == null) throw new ArgumentNullException(nameof(xd));
if (parent == null) throw new ArgumentNullException(nameof(parent)); if (parent == null) throw new ArgumentNullException(nameof(parent));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", 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));
var child = parent.SelectSingleNode(name) ?? xd.CreateNode(XmlNodeType.Element, name, ""); var child = parent.SelectSingleNode(name) ?? xd.CreateNode(XmlNodeType.Element, name, "");
child.InnerXml = value; child.InnerXml = value;
@@ -305,8 +308,9 @@ namespace Umbraco.Core.Xml
/// <returns>A XmlNode</returns> /// <returns>A XmlNode</returns>
public static XmlNode AddCDataNode(XmlDocument xd, string name, string value) public static XmlNode AddCDataNode(XmlDocument xd, string name, string value)
{ {
if (xd == null) throw new ArgumentNullException("xd"); if (xd == null) throw new ArgumentNullException(nameof(xd));
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));
var temp = xd.CreateNode(XmlNodeType.Element, name, ""); var temp = xd.CreateNode(XmlNodeType.Element, name, "");
temp.AppendChild(xd.CreateCDataSection(value)); temp.AppendChild(xd.CreateCDataSection(value));
@@ -323,9 +327,10 @@ namespace Umbraco.Core.Xml
/// <returns>a XmlNode</returns> /// <returns>a XmlNode</returns>
public static XmlNode SetCDataNode(XmlDocument xd, XmlNode parent, string name, string value) public static XmlNode SetCDataNode(XmlDocument xd, XmlNode parent, string name, string value)
{ {
if (xd == null) throw new ArgumentNullException("xd"); if (xd == null) throw new ArgumentNullException(nameof(xd));
if (parent == null) throw new ArgumentNullException("parent"); if (parent == null) throw new ArgumentNullException(nameof(parent));
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));
var child = parent.SelectSingleNode(name); var child = parent.SelectSingleNode(name);
if (child != null) if (child != null)

View File

@@ -10,7 +10,7 @@ namespace Umbraco.Examine
/// <summary> /// <summary>
/// Performs the data lookups required to rebuild a media index /// Performs the data lookups required to rebuild a media index
/// </summary> /// </summary>
public class MediaIndexPopulator : IndexPopulator<UmbracoContentIndex> public class MediaIndexPopulator : IndexPopulator<IUmbracoContentIndex>
{ {
private readonly int? _parentId; private readonly int? _parentId;
private readonly IMediaService _mediaService; private readonly IMediaService _mediaService;
@@ -69,6 +69,6 @@ namespace Umbraco.Examine
pageIndex++; pageIndex++;
} while (media.Length == pageSize); } while (media.Length == pageSize);
} }
} }
} }

View File

@@ -1,9 +1,13 @@
using Examine; using System;
using Examine;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using Umbraco.Core; using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Services; using Umbraco.Core.Services;
using Umbraco.Core.Strings; using Umbraco.Core.Strings;
@@ -13,14 +17,16 @@ namespace Umbraco.Examine
{ {
private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly ILogger _logger;
public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, public MediaValueSetBuilder(PropertyEditorCollection propertyEditors,
UrlSegmentProviderCollection urlSegmentProviders, UrlSegmentProviderCollection urlSegmentProviders,
IUserService userService) IUserService userService, ILogger logger)
: base(propertyEditors, false) : base(propertyEditors, false)
{ {
_urlSegmentProviders = urlSegmentProviders; _urlSegmentProviders = urlSegmentProviders;
_userService = userService; _userService = userService;
_logger = logger;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -29,6 +35,42 @@ namespace Umbraco.Examine
foreach (var m in media) foreach (var m in media)
{ {
var urlValue = m.GetUrlSegment(_urlSegmentProviders); var urlValue = m.GetUrlSegment(_urlSegmentProviders);
var umbracoFilePath = string.Empty;
var umbracoFile = string.Empty;
var umbracoFileSource = m.GetValue<string>(Constants.Conventions.Media.File);
if (umbracoFileSource.DetectIsJson())
{
ImageCropperValue cropper = null;
try
{
cropper = JsonConvert.DeserializeObject<ImageCropperValue>(
m.GetValue<string>(Constants.Conventions.Media.File));
}
catch (Exception ex)
{
_logger.Error<MediaValueSetBuilder>(ex, $"Could not Deserialize ImageCropperValue for item with key {m.Key} ");
}
if (cropper != null)
{
umbracoFilePath = cropper.Src;
}
}
else
{
umbracoFilePath = umbracoFileSource;
}
if (!string.IsNullOrEmpty(umbracoFilePath))
{
// intentional dummy Uri
var uri = new Uri("https://localhost/" + umbracoFilePath);
umbracoFile = uri.Segments.Last();
}
var values = new Dictionary<string, IEnumerable<object>> var values = new Dictionary<string, IEnumerable<object>>
{ {
{"icon", m.ContentType.Icon?.Yield() ?? Enumerable.Empty<string>()}, {"icon", m.ContentType.Icon?.Yield() ?? Enumerable.Empty<string>()},
@@ -44,7 +86,8 @@ namespace Umbraco.Examine
{"urlName", urlValue?.Yield() ?? Enumerable.Empty<string>()}, {"urlName", urlValue?.Yield() ?? Enumerable.Empty<string>()},
{"path", m.Path?.Yield() ?? Enumerable.Empty<string>()}, {"path", m.Path?.Yield() ?? Enumerable.Empty<string>()},
{"nodeType", m.ContentType.Id.ToString().Yield() }, {"nodeType", m.ContentType.Id.ToString().Yield() },
{"creatorName", (m.GetCreatorProfile(_userService)?.Name ?? "??").Yield()} {"creatorName", (m.GetCreatorProfile(_userService)?.Name ?? "??").Yield()},
{UmbracoExamineIndex.UmbracoFileFieldName, umbracoFile.Yield()}
}; };
foreach (var property in m.Properties) foreach (var property in m.Properties)

View File

@@ -49,7 +49,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- note: NuGet deals with transitive references now --> <!-- note: NuGet deals with transitive references now -->
<PackageReference Include="Examine" Version="1.0.0" /> <PackageReference Include="Examine" Version="1.0.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub"> <PackageReference Include="Microsoft.SourceLink.GitHub">
<Version>1.0.0-beta2-19324-01</Version> <Version>1.0.0-beta2-19324-01</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -32,6 +32,7 @@ namespace Umbraco.Examine
/// </summary> /// </summary>
public const string IndexPathFieldName = SpecialFieldPrefix + "Path"; public const string IndexPathFieldName = SpecialFieldPrefix + "Path";
public const string NodeKeyFieldName = SpecialFieldPrefix + "Key"; public const string NodeKeyFieldName = SpecialFieldPrefix + "Key";
public const string UmbracoFileFieldName = "umbracoFileSrc";
public const string IconFieldName = SpecialFieldPrefix + "Icon"; public const string IconFieldName = SpecialFieldPrefix + "Icon";
public const string PublishedFieldName = SpecialFieldPrefix + "Published"; public const string PublishedFieldName = SpecialFieldPrefix + "Published";

View File

@@ -5,7 +5,6 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using Umbraco.Core; using Umbraco.Core;
using Umbraco.Core.Cache; using Umbraco.Core.Cache;
using Umbraco.Core.Compose;
using Umbraco.Core.Composing; using Umbraco.Core.Composing;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
@@ -13,8 +12,6 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Scoping; using Umbraco.Core.Scoping;
[assembly:DisableComposer(typeof(Umbraco.Tests.Components.ComponentTests.Composer26))]
namespace Umbraco.Tests.Components namespace Umbraco.Tests.Components
{ {
[TestFixture] [TestFixture]
@@ -65,10 +62,11 @@ namespace Umbraco.Tests.Components
public void Boot1A() public void Boot1A()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = TypeArray<Composer1, Composer2, Composer3, Composer4>(); var types = TypeArray<Composer1, Composer2, Composer3, Composer4>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
// 2 is Core and requires 4 // 2 is Core and requires 4
// 3 is User - goes away with RuntimeLevel.Unknown // 3 is User - goes away with RuntimeLevel.Unknown
@@ -104,10 +102,11 @@ namespace Umbraco.Tests.Components
public void Boot1B() public void Boot1B()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run));
var types = TypeArray<Composer1, Composer2, Composer3, Composer4>(); var types = TypeArray<Composer1, Composer2, Composer3, Composer4>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
// 2 is Core and requires 4 // 2 is Core and requires 4
// 3 is User - stays with RuntimeLevel.Run // 3 is User - stays with RuntimeLevel.Run
@@ -120,10 +119,11 @@ namespace Umbraco.Tests.Components
public void Boot2() public void Boot2()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = TypeArray<Composer20, Composer21>(); var types = TypeArray<Composer20, Composer21>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
// 21 is required by 20 // 21 is required by 20
// => reorder components accordingly // => reorder components accordingly
@@ -135,10 +135,11 @@ namespace Umbraco.Tests.Components
public void Boot3() public void Boot3()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = TypeArray<Composer22, Composer24, Composer25>(); var types = TypeArray<Composer22, Composer24, Composer25>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
// i23 requires 22 // i23 requires 22
// 24, 25 implement i23 // 24, 25 implement i23
@@ -152,10 +153,11 @@ namespace Umbraco.Tests.Components
public void BrokenRequire() public void BrokenRequire()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = TypeArray<Composer1, Composer2, Composer3>(); var types = TypeArray<Composer1, Composer2, Composer3>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
try try
{ {
@@ -175,10 +177,11 @@ namespace Umbraco.Tests.Components
public void BrokenRequired() public void BrokenRequired()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = TypeArray<Composer2, Composer4, Composer13>(); var types = TypeArray<Composer2, Composer4, Composer13>();
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
// 2 is Core and requires 4 // 2 is Core and requires 4
// 13 is required by 1 // 13 is required by 1
@@ -196,6 +199,7 @@ namespace Umbraco.Tests.Components
Terminated.Clear(); Terminated.Clear();
var register = MockRegister(); var register = MockRegister();
var typeLoader = MockTypeLoader();
var factory = MockFactory(m => var factory = MockFactory(m =>
{ {
m.Setup(x => x.TryGetInstance(It.Is<Type>(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource()); m.Setup(x => x.TryGetInstance(It.Is<Type>(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource());
@@ -210,10 +214,10 @@ namespace Umbraco.Tests.Components
throw new NotSupportedException(type.FullName); throw new NotSupportedException(type.FullName);
}); });
}); });
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer1), typeof(Composer5), typeof(Composer5a) }; var types = new[] { typeof(Composer1), typeof(Composer5), typeof(Composer5a) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Assert.IsEmpty(Composed); Assert.IsEmpty(Composed);
composers.Compose(); composers.Compose();
@@ -236,10 +240,11 @@ namespace Umbraco.Tests.Components
public void Requires1() public void Requires1()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(2, Composed.Count); Assert.AreEqual(2, Composed.Count);
@@ -251,10 +256,11 @@ namespace Umbraco.Tests.Components
public void Requires2A() public void Requires2A()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(2, Composed.Count); Assert.AreEqual(2, Composed.Count);
@@ -267,11 +273,12 @@ namespace Umbraco.Tests.Components
public void Requires2B() public void Requires2B()
{ {
var register = MockRegister(); var register = MockRegister();
var typeLoader = MockTypeLoader();
var factory = MockFactory(); var factory = MockFactory();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run)); var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run));
var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
var builder = composition.WithCollectionBuilder<ComponentCollectionBuilder>(); var builder = composition.WithCollectionBuilder<ComponentCollectionBuilder>();
@@ -287,35 +294,36 @@ namespace Umbraco.Tests.Components
public void WeakDependencies() public void WeakDependencies()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer10) }; var types = new[] { typeof(Composer10) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(1, Composed.Count); Assert.AreEqual(1, Composed.Count);
Assert.AreEqual(typeof(Composer10), Composed[0]); Assert.AreEqual(typeof(Composer10), Composed[0]);
types = new[] { typeof(Composer11) }; types = new[] { typeof(Composer11) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose()); Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:"); Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements(false); var requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements)); Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer2) }; types = new[] { typeof(Composer2) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose()); Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:"); Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
requirements = composers.GetRequirements(false); requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements)); Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer12) }; types = new[] { typeof(Composer12) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(1, Composed.Count); Assert.AreEqual(1, Composed.Count);
@@ -326,10 +334,11 @@ namespace Umbraco.Tests.Components
public void DisableMissing() public void DisableMissing()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(2, Composed.Count); Assert.AreEqual(2, Composed.Count);
@@ -341,16 +350,18 @@ namespace Umbraco.Tests.Components
public void AttributesPriorities() public void AttributesPriorities()
{ {
var register = MockRegister(); var register = MockRegister();
var composition = new Composition(register, MockTypeLoader(), Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown)); var typeLoader = MockTypeLoader();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Unknown));
var types = new[] { typeof(Composer26) }; // 26 disabled by assembly attribute var types = new[] { typeof(Composer26) };
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var enableDisableAttributes = new[] { new DisableComposerAttribute(typeof(Composer26)) };
var composers = new Composers(composition, types, enableDisableAttributes, Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(0, Composed.Count); // 26 gone Assert.AreEqual(0, Composed.Count); // 26 gone
types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27 types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); composers = new Composers(composition, types, enableDisableAttributes, Mock.Of<IProfilingLogger>());
Composed.Clear(); Composed.Clear();
composers.Compose(); composers.Compose();
Assert.AreEqual(2, Composed.Count); // both Assert.AreEqual(2, Composed.Count); // both
@@ -367,7 +378,7 @@ namespace Umbraco.Tests.Components
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run)); var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run));
var types = typeLoader.GetTypes<IComposer>().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); var types = typeLoader.GetTypes<IComposer>().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web"));
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>()); var composers = new Composers(composition, types, Enumerable.Empty<Attribute>(), Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements(); var requirements = composers.GetRequirements();
var report = Composers.GetComposersReport(requirements); var report = Composers.GetComposersReport(requirements);
Console.WriteLine(report); Console.WriteLine(report);
@@ -506,7 +517,6 @@ namespace Umbraco.Tests.Components
public class Composer25 : TestComposerBase, IComposer23 public class Composer25 : TestComposerBase, IComposer23
{ } { }
// disabled by assembly attribute
public class Composer26 : TestComposerBase public class Composer26 : TestComposerBase
{ } { }

View File

@@ -51,5 +51,47 @@ namespace Umbraco.Tests.CoreThings
else else
Assert.IsFalse(value.HasFlagAny(test)); Assert.IsFalse(value.HasFlagAny(test));
} }
[TestCase(TreeUse.None, TreeUse.None, TreeUse.None)]
[TestCase(TreeUse.None, TreeUse.Main, TreeUse.Main)]
[TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.Dialog)]
[TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)]
[TestCase(TreeUse.Main, TreeUse.Main, TreeUse.Main)]
[TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
public void SetFlagTests(TreeUse value, TreeUse flag, TreeUse expected)
{
Assert.AreEqual(expected, value.SetFlag(flag));
}
[TestCase(TreeUse.None, TreeUse.None, TreeUse.None)]
[TestCase(TreeUse.None, TreeUse.Main, TreeUse.None)]
[TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.None)]
[TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
[TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)]
[TestCase(TreeUse.Main, TreeUse.Main, TreeUse.None)]
[TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main)]
[TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
[TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)]
[TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.None)]
[TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main)]
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
public void UnsetFlagTests(TreeUse value, TreeUse flag, TreeUse expected)
{
Assert.AreEqual(expected, value.UnsetFlag(flag));
}
} }
} }

View File

@@ -101,7 +101,7 @@ namespace Umbraco.Tests.IO
Assert.AreEqual(Path.Combine(basePath, @"foo\bar.tmp"), path); Assert.AreEqual(Path.Combine(basePath, @"foo\bar.tmp"), path);
// that path is invalid as it would be outside the root directory // that path is invalid as it would be outside the root directory
Assert.Throws<FileSecurityException>(() => _fileSystem.GetFullPath("../../foo.tmp")); Assert.Throws<UnauthorizedAccessException>(() => _fileSystem.GetFullPath("../../foo.tmp"));
// a very long path, which ends up being very long, works // a very long path, which ends up being very long, works
path = Repeat("bah/bah/", 50); path = Repeat("bah/bah/", 50);

View File

@@ -238,7 +238,7 @@ namespace Umbraco.Tests.IO
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore"); var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
var ss = new ShadowFileSystem(fs, sfs); var ss = new ShadowFileSystem(fs, sfs);
Assert.Throws<FileSecurityException>(() => Assert.Throws<UnauthorizedAccessException>(() =>
{ {
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
ss.AddFile("../../f1.txt", ms); ss.AddFile("../../f1.txt", ms);

View File

@@ -39,7 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
return scopeProvider?.Context?.GetEnlisted<SafeXmlReaderWriter>(EnlistKey); return scopeProvider?.Context?.GetEnlisted<SafeXmlReaderWriter>(EnlistKey);
} }
public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, AsyncLock xmlLock, XmlDocument xml, Action<XmlDocument> refresh, Action<XmlDocument, bool> apply, bool writer) public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, SystemLock xmlLock, XmlDocument xml, Action<XmlDocument> refresh, Action<XmlDocument, bool> apply, bool writer)
{ {
var scopeContext = scopeProvider.Context; var scopeContext = scopeProvider.Context;

View File

@@ -305,7 +305,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
private XmlDocument _xmlDocument; // supplied xml document (for tests) private XmlDocument _xmlDocument; // supplied xml document (for tests)
private volatile XmlDocument _xml; // master xml document private volatile XmlDocument _xml; // master xml document
private readonly AsyncLock _xmlLock = new AsyncLock(); // protects _xml private readonly SystemLock _xmlLock = new SystemLock(); // protects _xml
// to be used by PublishedContentCache only // to be used by PublishedContentCache only
// for non-preview content only // for non-preview content only

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
private bool _released; private bool _released;
private Timer _timer; private Timer _timer;
private DateTime _initialTouch; private DateTime _initialTouch;
private readonly AsyncLock _runLock = new AsyncLock(); // ensure we run once at a time private readonly SystemLock _runLock = new SystemLock(); // ensure we run once at a time
// note: // note:
// as long as the runner controls the runs, we know that we run once at a time, but // as long as the runner controls the runs, we know that we run once at a time, but

View File

@@ -621,7 +621,9 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation() Validation = new PropertyTypeValidation()
{ {
Mandatory = true, Mandatory = true,
Pattern = "xyz" MandatoryMessage = "Please enter a value",
Pattern = "xyz",
PatternMessage = "Please match the pattern",
} }
}; };
@@ -634,7 +636,9 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(basic.DataTypeId, result.DataTypeId); Assert.AreEqual(basic.DataTypeId, result.DataTypeId);
Assert.AreEqual(basic.Label, result.Name); Assert.AreEqual(basic.Label, result.Name);
Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory); Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory);
Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage);
Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp); Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp);
Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage);
} }
[Test] [Test]
@@ -655,7 +659,9 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation() Validation = new PropertyTypeValidation()
{ {
Mandatory = true, Mandatory = true,
Pattern = "xyz" MandatoryMessage = "Please enter a value",
Pattern = "xyz",
PatternMessage = "Please match the pattern",
} }
}; };
@@ -668,7 +674,9 @@ namespace Umbraco.Tests.Models.Mapping
Assert.AreEqual(basic.DataTypeId, result.DataTypeId); Assert.AreEqual(basic.DataTypeId, result.DataTypeId);
Assert.AreEqual(basic.Label, result.Name); Assert.AreEqual(basic.Label, result.Name);
Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory); Assert.AreEqual(basic.Validation.Mandatory, result.Mandatory);
Assert.AreEqual(basic.Validation.MandatoryMessage, result.MandatoryMessage);
Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp); Assert.AreEqual(basic.Validation.Pattern, result.ValidationRegExp);
Assert.AreEqual(basic.Validation.PatternMessage, result.ValidationRegExpMessage);
} }
[Test] [Test]
@@ -951,7 +959,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1", Name = "Tab 1",
SortOrder = 0, SortOrder = 0,
Inherited = false, Inherited = false,
Properties = new [] Properties = new[]
{ {
new MemberPropertyTypeBasic new MemberPropertyTypeBasic
{ {
@@ -965,7 +973,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1000,7 +1008,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1", Name = "Tab 1",
SortOrder = 0, SortOrder = 0,
Inherited = false, Inherited = false,
Properties = new [] Properties = new[]
{ {
new PropertyTypeBasic new PropertyTypeBasic
{ {
@@ -1011,7 +1019,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1052,7 +1060,7 @@ namespace Umbraco.Tests.Models.Mapping
Name = "Tab 1", Name = "Tab 1",
SortOrder = 0, SortOrder = 0,
Inherited = false, Inherited = false,
Properties = new [] Properties = new[]
{ {
new PropertyTypeBasic new PropertyTypeBasic
{ {
@@ -1063,7 +1071,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1109,7 +1117,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1133,7 +1141,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1187,7 +1195,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555
@@ -1211,7 +1219,7 @@ namespace Umbraco.Tests.Models.Mapping
Validation = new PropertyTypeValidation Validation = new PropertyTypeValidation
{ {
Mandatory = false, Mandatory = false,
Pattern = "" Pattern = string.Empty
}, },
SortOrder = 0, SortOrder = 0,
DataTypeId = 555 DataTypeId = 555

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