From 78b7bb6a2299244b410b9036d79125366c5b9651 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 8 Nov 2018 15:33:14 +0000 Subject: [PATCH 01/10] Recreate benchmark project and update deps Fix #3561 --- .../Config/QuickRunConfigAttribute.cs | 13 +- .../CtorInvokeBenchmarks.cs | 2 +- src/Umbraco.Tests.Benchmarks/Program.cs | 7 +- .../Properties/AssemblyInfo.cs | 4 +- .../TryConvertToBenchmarks.cs | 8 +- .../Umbraco.Tests.Benchmarks.csproj | 110 ++----- src/Umbraco.Tests.Benchmarks/app.config | 300 +----------------- src/umbraco.sln | 14 +- 8 files changed, 52 insertions(+), 406 deletions(-) diff --git a/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs b/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs index f7d6b6bb72..52d670de3c 100644 --- a/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs +++ b/src/Umbraco.Tests.Benchmarks/Config/QuickRunConfigAttribute.cs @@ -1,13 +1,14 @@ -using BenchmarkDotNet.Configs; +using System; +using BenchmarkDotNet.Configs; using BenchmarkDotNet.Horology; using BenchmarkDotNet.Jobs; -using System; namespace Umbraco.Tests.Benchmarks.Config { /// /// Configures the benchmark to run with less warmup and a shorter iteration time than the standard benchmark. /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class QuickRunConfigAttribute : Attribute, IConfigSource { /// @@ -15,11 +16,11 @@ namespace Umbraco.Tests.Benchmarks.Config /// public QuickRunConfigAttribute() { - Config = (ManualConfig) ManualConfig.CreateEmpty() + this.Config = (ManualConfig)ManualConfig.CreateEmpty() .With(Job.Default.WithLaunchCount(1) // benchmark process will be launched only once .WithIterationTime(new TimeInterval(100, TimeUnit.Millisecond)) // 100ms per iteration .WithWarmupCount(3) // 3 warmup iteration - .WithTargetCount(3)); // 3 target iteration + .WithIterationCount(3)); // 3 target iteration } /// @@ -28,6 +29,6 @@ namespace Umbraco.Tests.Benchmarks.Config protected ManualConfig Config { get; } /// - IConfig IConfigSource.Config => Config; + IConfig IConfigSource.Config => this.Config; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs index 1d5876187b..5588e13d12 100644 --- a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Benchmarks .WithLaunchCount(1) // benchmark process will be launched only once .WithIterationTime(TimeInterval.FromMilliseconds(400)) .WithWarmupCount(3) - .WithTargetCount(6)); + .WithIterationCount(6)); } } diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs index c9332e7fa3..62137bd85d 100644 --- a/src/Umbraco.Tests.Benchmarks/Program.cs +++ b/src/Umbraco.Tests.Benchmarks/Program.cs @@ -2,11 +2,8 @@ namespace Umbraco.Tests.Benchmarks { - internal class Program + internal static class Program { - public static void Main(string[] args) - { - new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); - } + private static void Main(string[] args) => new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); } } diff --git a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs index 2ab0051c26..9f5a3c7453 100644 --- a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Umbraco.Tests.Benchmarks")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,7 +20,7 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("86deb346-089f-4106-89c8-d852b9cf2a33")] +[assembly: Guid("3a33adc9-c6c0-4db1-a613-a9af0210df3d")] // Version information for an assembly consists of the following four values: // diff --git a/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs index 57b47dc1d0..7e73c5e438 100644 --- a/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TryConvertToBenchmarks.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; using Umbraco.Core; namespace Umbraco.Tests.Benchmarks @@ -12,9 +14,9 @@ namespace Umbraco.Tests.Benchmarks private static readonly string Date = "Saturday 10, November 2012"; [Benchmark(Description = "List to IEnumerable")] - public IEnumerable TryConvertToEnumerable() + public IList TryConvertToEnumerable() { - return List.TryConvertTo>().Result; + return List.TryConvertTo>().Result.ToList(); } [Benchmark(Description = "Int to Double")] @@ -41,4 +43,4 @@ namespace Umbraco.Tests.Benchmarks return Date.TryConvertTo().Result; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 9755e4f9db..99bb768842 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -1,20 +1,17 @@  - + Debug AnyCPU - {86DEB346-089F-4106-89C8-D852B9CF2A33} + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} Exe - Properties Umbraco.Tests.Benchmarks Umbraco.Tests.Benchmarks v4.7.2 512 - - - - false + true + true AnyCPU @@ -25,7 +22,6 @@ DEBUG;TRACE prompt 4 - latest AnyCPU @@ -35,92 +31,27 @@ TRACE prompt 4 - latest - - - Always + false + 7.3 - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - @@ -130,27 +61,34 @@ - + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} Umbraco.Core + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + Umbraco.Examine + {5d3b8245-ada6-453f-a008-50ed04bfe770} Umbraco.Tests + + {4c4c194c-b5e4-4991-8f87-4373e24cc19f} + Umbraco.Web.UI + {651e1350-91b6-44b7-bd60-7207006d7003} Umbraco.Web - - - $(NuGetPackageFolders.Split(';')[0]) - + + + 0.11.2 + + - - - \ No newline at end of file + diff --git a/src/Umbraco.Tests.Benchmarks/app.config b/src/Umbraco.Tests.Benchmarks/app.config index b5e577b22c..56efbc7b5f 100644 --- a/src/Umbraco.Tests.Benchmarks/app.config +++ b/src/Umbraco.Tests.Benchmarks/app.config @@ -1,298 +1,6 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/umbraco.sln b/src/umbraco.sln index ceb687b876..41bd8359c3 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -82,8 +82,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5B03EF4E ..\build\NuSpecs\build\UmbracoCms.targets = ..\build\NuSpecs\build\UmbracoCms.targets EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{86DEB346-089F-4106-89C8-D852B9CF2A33}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocTools", "DocTools", "{53594E5B-64A2-4545-8367-E3627D266AE8}" ProjectSection(SolutionItems) = preProject ApiDocs\docfx.filter.yml = ApiDocs\docfx.filter.yml @@ -92,6 +90,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocTools", "DocTools", "{53 ApiDocs\toc.yml = ApiDocs\toc.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -120,10 +120,10 @@ Global {07FBC26B-2927-4A22-8D96-D644C667FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU {07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.Build.0 = Release|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.Build.0 = Release|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -133,8 +133,8 @@ Global {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} {5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} - {86DEB346-089F-4106-89C8-D852B9CF2A33} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {53594E5B-64A2-4545-8367-E3627D266AE8} = {FD962632-184C-4005-A5F3-E705D92FC645} + {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC} From a8fc62cf4242a7bc4f6ae4d7cf2768a6aec30ed2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 12 Nov 2018 16:22:33 +0000 Subject: [PATCH 02/10] Optimize Guid.Combine and HexString generation. --- src/Umbraco.Core/ByteArrayExtensions.cs | 39 --------- src/Umbraco.Core/GuidUtils.cs | 44 ++++++++++ src/Umbraco.Core/HexEncoder.cs | 84 +++++++++++++++++++ .../CombinedGuidsMediaPathScheme.cs | 17 +--- src/Umbraco.Core/Umbraco.Core.csproj | 3 +- .../CombineGuidBenchmarks.cs | 48 +++++++++++ .../HexStringBenchmarks.cs | 69 +++++++++++++++ .../Umbraco.Tests.Benchmarks.csproj | 4 +- .../CoreThings/GuidUtilsTests.cs | 32 +++++++ .../CoreThings/HexEncoderTests.cs | 71 ++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + 11 files changed, 357 insertions(+), 56 deletions(-) delete mode 100644 src/Umbraco.Core/ByteArrayExtensions.cs create mode 100644 src/Umbraco.Core/GuidUtils.cs create mode 100644 src/Umbraco.Core/HexEncoder.cs create mode 100644 src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs create mode 100644 src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs create mode 100644 src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs create mode 100644 src/Umbraco.Tests/CoreThings/HexEncoderTests.cs diff --git a/src/Umbraco.Core/ByteArrayExtensions.cs b/src/Umbraco.Core/ByteArrayExtensions.cs deleted file mode 100644 index dacdd509ca..0000000000 --- a/src/Umbraco.Core/ByteArrayExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Umbraco.Core -{ - public static class ByteArrayExtensions - { - private static readonly char[] BytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - public static string ToHexString(this byte[] bytes) - { - int i = 0, p = 0, bytesLength = bytes.Length; - var chars = new char[bytesLength * 2]; - while (i < bytesLength) - { - var b = bytes[i++]; - chars[p++] = BytesToHexStringLookup[b / 0x10]; - chars[p++] = BytesToHexStringLookup[b % 0x10]; - } - return new string(chars, 0, chars.Length); - } - - public static string ToHexString(this byte[] bytes, char separator, int blockSize, int blockCount) - { - int p = 0, bytesLength = bytes.Length, count = 0, size = 0; - var chars = new char[bytesLength * 2 + blockCount]; - for (var i = 0; i < bytesLength; i++) - { - var b = bytes[i++]; - chars[p++] = BytesToHexStringLookup[b / 0x10]; - chars[p++] = BytesToHexStringLookup[b % 0x10]; - if (count == blockCount) continue; - if (++size < blockSize) continue; - - chars[p++] = '/'; - size = 0; - count++; - } - return new string(chars, 0, chars.Length); - } - } -} diff --git a/src/Umbraco.Core/GuidUtils.cs b/src/Umbraco.Core/GuidUtils.cs new file mode 100644 index 0000000000..3768e1385d --- /dev/null +++ b/src/Umbraco.Core/GuidUtils.cs @@ -0,0 +1,44 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Umbraco.Core +{ + /// + /// Utility methods for the struct. + /// + internal static class GuidUtils + { + /// + /// Combines two guid instances utilizing an exclusive disjunction. + /// The resultant guid is not guaranteed to be unique since the number of unique bits is halved. + /// + /// The first guid. + /// The seconds guid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Guid Combine(Guid a, Guid b) + { + var ad = new DecomposedGuid(a); + var bd = new DecomposedGuid(b); + + ad.Hi ^= bd.Hi; + ad.Lo ^= bd.Lo; + + return ad.Value; + } + + /// + /// A decomposed guid. Allows access to the high and low bits without unsafe code. + /// + [StructLayout(LayoutKind.Explicit)] + private struct DecomposedGuid + { + [FieldOffset(00)] public Guid Value; + [FieldOffset(00)] public long Hi; + [FieldOffset(08)] public long Lo; + + public DecomposedGuid(Guid value) : this() => this.Value = value; + } + } +} diff --git a/src/Umbraco.Core/HexEncoder.cs b/src/Umbraco.Core/HexEncoder.cs new file mode 100644 index 0000000000..073dc8b543 --- /dev/null +++ b/src/Umbraco.Core/HexEncoder.cs @@ -0,0 +1,84 @@ +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Umbraco.Core +{ + /// + /// Provides methods for encoding byte arrays into hexidecimal strings. + /// + internal static class HexEncoder + { + // LUT's that provide the hexidecimal representation of each possible byte value. + private static readonly char[] HexLutBase = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + // The base LUT arranged in 16x each item order. 0 * 16, 1 * 16, .... F * 16 + private static readonly char[] HexLutHi = Enumerable.Range(0, 256).Select(x => HexLutBase[x / 0x10]).ToArray(); + + // The base LUT repeated 16x. + private static readonly char[] HexLutLo = Enumerable.Range(0, 256).Select(x => HexLutBase[x % 0x10]).ToArray(); + + /// + /// Converts a to a hexidecimal formatted padded to 2 digits. + /// + /// The bytes. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Encode(byte[] bytes) + { + var length = bytes.Length; + var chars = new char[length * 2]; + + var index = 0; + for (var i = 0; i < length; i++) + { + var byteIndex = bytes[i]; + chars[index++] = HexLutHi[byteIndex]; + chars[index++] = HexLutLo[byteIndex]; + } + + return new string(chars, 0, chars.Length); + } + + /// + /// Converts a to a hexidecimal formatted padded to 2 digits + /// and split into blocks with the given char separator. + /// + /// The bytes. + /// The separator. + /// The block size. + /// The block count. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Encode(byte[] bytes, char separator, int blockSize, int blockCount) + { + var length = bytes.Length; + var chars = new char[(length * 2) + blockCount]; + var count = 0; + var size = 0; + var index = 0; + + for (var i = 0; i < length; i++) + { + var byteIndex = bytes[i]; + chars[index++] = HexLutHi[byteIndex]; + chars[index++] = HexLutLo[byteIndex]; + + if (count == blockCount) + { + continue; + } + + if (++size < blockSize) + { + continue; + } + + chars[index++] = separator; + size = 0; + count++; + } + + return new string(chars, 0, chars.Length); + } + } +} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index ef71aff3bc..37c6a7b209 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -20,24 +20,11 @@ namespace Umbraco.Core.IO.MediaPathSchemes { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name - var directory = Combine(itemGuid, propertyGuid).ToHexString(/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/... + var directory = HexEncoder.Encode(GuidUtils.Combine(itemGuid, propertyGuid).ToByteArray()/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/... return Path.Combine(directory, filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(string filepath) - { - return Path.GetDirectoryName(filepath); - } - - private static byte[] Combine(Guid guid1, Guid guid2) - { - var bytes1 = guid1.ToByteArray(); - var bytes2 = guid2.ToByteArray(); - var bytes = new byte[bytes1.Length]; - for (var i = 0; i < bytes1.Length; i++) - bytes[i] = (byte) (bytes1[i] ^ bytes2[i]); - return bytes; - } + public string GetDeleteDirectory(string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ac115e843b..49fa197850 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -111,7 +111,6 @@ - @@ -320,6 +319,8 @@ + + diff --git a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs new file mode 100644 index 0000000000..ce55f6890d --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs @@ -0,0 +1,48 @@ +using System; +using BenchmarkDotNet.Attributes; +using Umbraco.Core; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunWithMemoryDiagnoserConfig] + public class CombineGuidBenchmarks + { + private static readonly Guid _a = Guid.NewGuid(); + private static readonly Guid _b = Guid.NewGuid(); + + [Benchmark] + public byte[] CombineUtils() => GuidUtils.Combine(_a, _b).ToByteArray(); + + [Benchmark] + public byte[] CombineLoop() => Combine(_a, _b); + + private static byte[] Combine(Guid guid1, Guid guid2) + { + var bytes1 = guid1.ToByteArray(); + var bytes2 = guid2.ToByteArray(); + var bytes = new byte[bytes1.Length]; + for (var i = 0; i < bytes1.Length; i++) + { + bytes[i] = (byte)(bytes1[i] ^ bytes2[i]); + } + + return bytes; + } + } + + // Nov 8 2018 + //BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17763.55 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores + // [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + // Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + + //IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1 + //WarmupCount=3 + + // Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | + //------------- |---------:|----------:|----------:|------------:|------------:|------------:|--------------------:| + // CombineUtils | 33.34 ns | 8.086 ns | 0.4432 ns | 0.0133 | - | - | 28 B | + // CombineLoop | 55.03 ns | 11.311 ns | 0.6200 ns | 0.0395 | - | - | 84 B | +} + diff --git a/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs new file mode 100644 index 0000000000..e29a5a24f3 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/HexStringBenchmarks.cs @@ -0,0 +1,69 @@ +using System; +using System.Text; +using BenchmarkDotNet.Attributes; +using Umbraco.Core; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunConfig] + public class HexStringBenchmarks + { + private byte[] _buffer; + + [Params(8, 16, 32, 64, 128, 256)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this._buffer = new byte[this.Count]; + var random = new Random(); + random.NextBytes(this._buffer); + } + + [Benchmark(Baseline = true)] + public string ToHexStringBuilder() + { + var sb = new StringBuilder(this._buffer.Length * 2); + for (var i = 0; i < this._buffer.Length; i++) + { + sb.Append(this._buffer[i].ToString("X2")); + } + + return sb.ToString(); + } + + [Benchmark] + public string ToHexStringEncoder() => HexEncoder.Encode(this._buffer); + } + + // Nov 8 2018 + //BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17763.55 (1809/October2018Update/Redstone5) + //Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores + // [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + // Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3190.0 + + //IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1 + //WarmupCount=3 + + // Method | Count | Mean | Error | StdDev | Ratio | + //------------------- |------ |-------------:|-------------:|-----------:|------:| + // ToHexStringBuilder | 8 | 786.49 ns | 319.92 ns | 17.536 ns | 1.00 | + // ToHexStringEncoder | 8 | 64.19 ns | 30.21 ns | 1.656 ns | 0.08 | + // | | | | | | + // ToHexStringBuilder | 16 | 1,442.43 ns | 503.00 ns | 27.571 ns | 1.00 | + // ToHexStringEncoder | 16 | 133.46 ns | 177.55 ns | 9.732 ns | 0.09 | + // | | | | | | + // ToHexStringBuilder | 32 | 2,869.23 ns | 924.35 ns | 50.667 ns | 1.00 | + // ToHexStringEncoder | 32 | 181.03 ns | 96.64 ns | 5.297 ns | 0.06 | + // | | | | | | + // ToHexStringBuilder | 64 | 5,775.33 ns | 2,825.42 ns | 154.871 ns | 1.00 | + // ToHexStringEncoder | 64 | 331.16 ns | 125.63 ns | 6.886 ns | 0.06 | + // | | | | | | + // ToHexStringBuilder | 128 | 11,662.35 ns | 4,908.03 ns | 269.026 ns | 1.00 | + // ToHexStringEncoder | 128 | 633.78 ns | 57.56 ns | 3.155 ns | 0.05 | + // | | | | | | + // ToHexStringBuilder | 256 | 22,960.11 ns | 14,111.47 ns | 773.497 ns | 1.00 | + // ToHexStringEncoder | 256 | 1,224.76 ns | 547.27 ns | 29.998 ns | 0.05 | +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 99bb768842..e5bc3e88b0 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -46,10 +46,12 @@ + + @@ -91,4 +93,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs b/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs new file mode 100644 index 0000000000..5ef8cba356 --- /dev/null +++ b/src/Umbraco.Tests/CoreThings/GuidUtilsTests.cs @@ -0,0 +1,32 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.CoreThings +{ + public class GuidUtilsTests + { + [Test] + public void GuidCombineMethodsAreEqual() + { + var a = Guid.NewGuid(); + var b = Guid.NewGuid(); + + Assert.AreEqual(GuidUtils.Combine(a, b).ToByteArray(), Combine(a, b)); + } + + // Reference implementation taken from original code. + private static byte[] Combine(Guid guid1, Guid guid2) + { + var bytes1 = guid1.ToByteArray(); + var bytes2 = guid2.ToByteArray(); + var bytes = new byte[bytes1.Length]; + for (var i = 0; i < bytes1.Length; i++) + { + bytes[i] = (byte)(bytes1[i] ^ bytes2[i]); + } + + return bytes; + } + } +} diff --git a/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs b/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs new file mode 100644 index 0000000000..588fff83e8 --- /dev/null +++ b/src/Umbraco.Tests/CoreThings/HexEncoderTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Text; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.CoreThings +{ + public class HexEncoderTests + { + [Test] + public void ToHexStringCreatesCorrectValue() + { + var buffer = new byte[255]; + var random = new Random(); + random.NextBytes(buffer); + + var sb = new StringBuilder(buffer.Length * 2); + for (var i = 0; i < buffer.Length; i++) + { + sb.Append(buffer[i].ToString("X2")); + } + + var expected = sb.ToString(); + + var actual = HexEncoder.Encode(buffer); + Assert.AreEqual(expected, actual); + } + + [Test] + public void ToHexStringWithSeparatorCreatesCorrectValue() + { + var buffer = new byte[255]; + var random = new Random(); + random.NextBytes(buffer); + + var expected = ToHexString(buffer, '/', 2, 4); + var actual = HexEncoder.Encode(buffer, '/', 2, 4); + + Assert.AreEqual(expected, actual); + } + + private static readonly char[] _bytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + // Reference implementation taken from original extension method. + private static string ToHexString(byte[] bytes, char separator, int blockSize, int blockCount) + { + int p = 0, bytesLength = bytes.Length, count = 0, size = 0; + var chars = new char[(bytesLength * 2) + blockCount]; + for (var i = 0; i < bytesLength; i++) + { + var b = bytes[i]; + chars[p++] = _bytesToHexStringLookup[b / 0x10]; + chars[p++] = _bytesToHexStringLookup[b % 0x10]; + if (count == blockCount) + { + continue; + } + + if (++size < blockSize) + { + continue; + } + + chars[p++] = separator; + size = 0; + count++; + } + return new string(chars, 0, chars.Length); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 156bc06a14..8d3da825f5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -118,6 +118,8 @@ + + From e173ea6dac38ff905a068ccc49c23173157e1cb1 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 15:30:56 +0100 Subject: [PATCH 03/10] Cleanup --- .../Implement/ContentTypeRepositoryBase.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 6be07a4c3d..9b62dbd6e3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -190,8 +190,8 @@ AND umbracoNode.nodeObjectType = @objectType", SortOrder = allowedContentType.SortOrder }); } - - + + //Insert Tabs foreach (var propertyGroup in entity.PropertyGroups) { @@ -331,11 +331,11 @@ AND umbracoNode.id <> @id", if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(pt => pt.IsDirty())) { var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id }); - var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id); + var dbPropertyTypeIds = dbPropertyTypes.Select(x => x.Id); var entityPropertyTypes = entity.PropertyTypes.Where(x => x.HasIdentity).Select(x => x.Id); - var items = dbPropertyTypeAlias.Except(entityPropertyTypes); - foreach (var item in items) - DeletePropertyType(entity.Id, item); + var propertyTypeToDeleteIds = dbPropertyTypeIds.Except(entityPropertyTypes); + foreach (var propertyTypeId in propertyTypeToDeleteIds) + DeletePropertyType(entity.Id, propertyTypeId); } // Delete tabs ... by excepting entries from db with entries from collections. @@ -620,7 +620,7 @@ AND umbracoNode.id <> @id", var sqlDelete = Sql() .Delete() .WhereIn((System.Linq.Expressions.Expression>)(x => x.ContentKey), sqlSelect); - + Database.Execute(sqlDelete); } @@ -690,7 +690,7 @@ AND umbracoNode.id <> @id", //first clear out any existing names that might already exists under the default lang //there's 2x tables to update - //clear out the versionCultureVariation table + //clear out the versionCultureVariation table var sqlSelect = Sql().Select(x => x.Id) .From() .InnerJoin().On(x => x.Id, x => x.VersionId) From c4cf843b82e549a9808abbbd5613ff970667a7fb Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 10 Dec 2018 14:59:17 +0000 Subject: [PATCH 04/10] Adds in the Umbraco Forms Promo dashboard in via package.manifest so that Forms install can override when installed --- .../App_Plugins/UmbracoForms/package.manifest | 10 ++++++++++ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 9 +++++---- src/Umbraco.Web.UI/config/Dashboard.config | 11 ----------- 3 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest diff --git a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest new file mode 100644 index 0000000000..c7ed4a957a --- /dev/null +++ b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest @@ -0,0 +1,10 @@ +{ + "dashboards": [ + { + "name": "Install Umbraco Forms", + "alias": "installUmbracoForms", + "view": "views/dashboard/forms/formsdashboardintro.html", + "sections": [ "forms" ] + } + ] +} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2823908a92..130015ad53 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -231,6 +231,7 @@ + 404handlers.config @@ -452,10 +453,10 @@ $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll - - $(ProgramFiles32)\Microsoft Visual Studio\2019\Preview\MSBuild\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll + + $(ProgramFiles32)\Microsoft Visual Studio\2019\Preview\MSBuild\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index fec6ab34ae..785fb61681 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -22,17 +22,6 @@ -
- - forms - - - - views/dashboard/forms/formsdashboardintro.html - - -
-
media From f10b86c9ec58018681d4df4e4a7a023da7c58dca Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 17:07:45 +0100 Subject: [PATCH 05/10] Fix RelationMapperProfiler --- .../Models/Mapping/RelationMapperProfile.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs index 1622fb907e..e31b1877d3 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapperProfile.cs @@ -11,18 +11,17 @@ namespace Umbraco.Web.Models.Mapping { // FROM IRelationType to RelationTypeDisplay CreateMap() - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Path, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()) - .ForMember(x => x.ChildObjectTypeName, expression => expression.Ignore()) - .ForMember(x => x.ParentObjectTypeName, expression => expression.Ignore()) - .ForMember(x => x.Relations, expression => expression.Ignore()) - .ForMember( - x => x.Udi, - expression => expression.MapFrom( - content => Udi.Create(Constants.UdiEntityType.RelationType, content.Key))) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .ForMember(dest => dest.ChildObjectTypeName, opt => opt.Ignore()) + .ForMember(dest => dest.ParentObjectTypeName, opt => opt.Ignore()) + .ForMember(dest => dest.Relations, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.RelationType, content.Key))) .AfterMap((src, dest) => { // Build up the path @@ -34,10 +33,15 @@ namespace Umbraco.Web.Models.Mapping }); // FROM IRelation to RelationDisplay - CreateMap(); + CreateMap() + .ForMember(dest => dest.ParentName, opt => opt.Ignore()) + .ForMember(dest => dest.ChildName, opt => opt.Ignore()); // FROM RelationTypeSave to IRelationType - CreateMap(); + CreateMap() + .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()) + .ForMember(dest => dest.DeleteDate, opt => opt.Ignore()); } } } From f9967e2d7d90733374746f8d0496d01d9034649b Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 10 Dec 2018 17:48:24 +0100 Subject: [PATCH 06/10] Fix e25e509d84 breaking tests, fix original issue --- src/Umbraco.Core/Models/ContentTypeBase.cs | 16 +++++++++------- src/Umbraco.Core/Models/PropertyGroup.cs | 7 +++++-- .../Implement/ContentTypeRepositoryBase.cs | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index caa63d7526..88b1179f6d 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -92,8 +92,8 @@ namespace Umbraco.Core.Models public readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo(x => x.AllowedAsRoot); public readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo(x => x.IsContainer); public readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); - public readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); - public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyTypes); + public readonly PropertyInfo PropertyGroupsSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); + public readonly PropertyInfo PropertyTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.PropertyTypes); public readonly PropertyInfo HasPropertyTypeBeenRemovedSelector = ExpressionHelper.GetPropertyInfo(x => x.HasPropertyTypeBeenRemoved); public readonly PropertyInfo VaryBy = ExpressionHelper.GetPropertyInfo(x => x.Variations); @@ -106,12 +106,12 @@ namespace Umbraco.Core.Models protected void PropertyGroupsChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyGroupCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyGroupsSelector); } protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } /// @@ -263,6 +263,8 @@ namespace Umbraco.Core.Models get => _noGroupPropertyTypes; set { + if (_noGroupPropertyTypes != null) + _noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; _noGroupPropertyTypes = new PropertyTypeCollection(IsPublishing, value); _noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged; PropertyTypesChanged(_noGroupPropertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); @@ -376,7 +378,7 @@ namespace Umbraco.Core.Models if (!HasPropertyTypeBeenRemoved) { HasPropertyTypeBeenRemoved = true; - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } break; } @@ -388,7 +390,7 @@ namespace Umbraco.Core.Models if (!HasPropertyTypeBeenRemoved) { HasPropertyTypeBeenRemoved = true; - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypesSelector); } } } @@ -412,7 +414,7 @@ namespace Umbraco.Core.Models // actually remove the group PropertyGroups.RemoveItem(propertyGroupName); - OnPropertyChanged(Ps.Value.PropertyGroupCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyGroupsSelector); } /// diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs index 1d0b949932..595e8d1d6a 100644 --- a/src/Umbraco.Core/Models/PropertyGroup.cs +++ b/src/Umbraco.Core/Models/PropertyGroup.cs @@ -35,12 +35,12 @@ namespace Umbraco.Core.Models { public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); - public readonly PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes); + public readonly PropertyInfo PropertyTypes = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes); } private void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e) { - OnPropertyChanged(Ps.Value.PropertyTypeCollectionSelector); + OnPropertyChanged(Ps.Value.PropertyTypes); } /// @@ -76,6 +76,8 @@ namespace Umbraco.Core.Models get => _propertyTypes; set { + if (_propertyTypes != null) + _propertyTypes.CollectionChanged -= PropertyTypesChanged; _propertyTypes = value; // since we're adding this collection to this group, @@ -83,6 +85,7 @@ namespace Umbraco.Core.Models foreach (var propertyType in _propertyTypes) propertyType.PropertyGroupId = new Lazy(() => Id); + OnPropertyChanged(Ps.Value.PropertyTypes); _propertyTypes.CollectionChanged += PropertyTypesChanged; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 9b62dbd6e3..44215b7f7e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -328,7 +328,7 @@ AND umbracoNode.id <> @id", // We check if the entity's own PropertyTypes has been modified and then also check // any of the property groups PropertyTypes has been modified. // This specifically tells us if any property type collections have changed. - if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(pt => pt.IsDirty())) + if (entity.IsPropertyDirty("NoGroupPropertyTypes") || entity.PropertyGroups.Any(x => x.IsPropertyDirty("PropertyTypes"))) { var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { entity.Id }); var dbPropertyTypeIds = dbPropertyTypes.Select(x => x.Id); From e91cf0039b9250645de6d9e7ca795df0bf56ae76 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 11 Dec 2018 12:32:51 +1100 Subject: [PATCH 07/10] Adds ability to filter the outgoing dashboard model --- src/Umbraco.Web/Editors/DashboardController.cs | 1 + src/Umbraco.Web/Editors/EditorModelEventManager.cs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 72b7acc9e7..d8cbc34938 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -118,6 +118,7 @@ namespace Umbraco.Web.Editors } [ValidateAngularAntiForgeryToken] + [OutgoingEditorModelEvent] public IEnumerable> GetDashboard(string section) { return _dashboards.GetDashboards(section, Security.CurrentUser); diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs index e2a248cb88..2225f5c577 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs @@ -1,4 +1,5 @@ -using System.Web.Http.Filters; +using System.Collections.Generic; +using System.Web.Http.Filters; using Umbraco.Core.Events; using Umbraco.Web.Models.ContentEditing; @@ -13,6 +14,13 @@ namespace Umbraco.Web.Editors public static event TypedEventHandler> SendingMediaModel; public static event TypedEventHandler> SendingMemberModel; public static event TypedEventHandler> SendingUserModel; + public static event TypedEventHandler>>> SendingDashboardModel; + + private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs>> e) + { + var handler = SendingDashboardModel; + handler?.Invoke(sender, e); + } private static void OnSendingUserModel(HttpActionExecutedContext sender, EditorModelEventArgs e) { @@ -56,6 +64,9 @@ namespace Umbraco.Web.Editors if (e.Model is UserDisplay) OnSendingUserModel(sender, new EditorModelEventArgs(e)); + + if (e.Model is IEnumerable>) + OnSendingDashboardModel(sender, new EditorModelEventArgs>>(e)); } } } From 9b52fa215f0eb11f7daa96dbb21e47b8d1b15942 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 11 Dec 2018 12:53:15 +1100 Subject: [PATCH 08/10] Allows replacing the model with the outgoing event args --- .../Editors/EditorModelEventArgs.cs | 21 +++++++++++++++---- .../OutgoingEditorModelEventAttribute.cs | 11 +++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs index 153a2d8786..daf262fce5 100644 --- a/src/Umbraco.Web/Editors/EditorModelEventArgs.cs +++ b/src/Umbraco.Web/Editors/EditorModelEventArgs.cs @@ -4,9 +4,13 @@ namespace Umbraco.Web.Editors { public sealed class EditorModelEventArgs : EditorModelEventArgs { + private readonly EditorModelEventArgs _baseArgs; + private T _model; + public EditorModelEventArgs(EditorModelEventArgs baseArgs) : base(baseArgs.Model, baseArgs.UmbracoContext) { + _baseArgs = baseArgs; Model = (T)baseArgs.Model; } @@ -16,7 +20,16 @@ namespace Umbraco.Web.Editors Model = model; } - public new T Model { get; private set; } + public new T Model + { + get => _model; + set + { + _model = value; + if (_baseArgs != null) + _baseArgs.Model = _model; + } + } } public class EditorModelEventArgs : EventArgs @@ -27,7 +40,7 @@ namespace Umbraco.Web.Editors UmbracoContext = umbracoContext; } - public object Model { get; private set; } - public UmbracoContext UmbracoContext { get; private set; } + public object Model { get; set; } + public UmbracoContext UmbracoContext { get; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs index ec32b61bca..8410891a5d 100644 --- a/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/OutgoingEditorModelEventAttribute.cs @@ -19,16 +19,17 @@ namespace Umbraco.Web.WebApi.Filters var user = UmbracoContext.Current.Security.CurrentUser; if (user == null) return; - var objectContent = actionExecutedContext.Response.Content as ObjectContent; - if (objectContent != null) + if (actionExecutedContext.Response.Content is ObjectContent objectContent) { var model = objectContent.Value; if (model != null) { - EditorModelEventManager.EmitEvent(actionExecutedContext, new EditorModelEventArgs( - (dynamic)model, - UmbracoContext.Current)); + var args = new EditorModelEventArgs( + model, + UmbracoContext.Current); + EditorModelEventManager.EmitEvent(actionExecutedContext, args); + objectContent.Value = args.Model; } } From 27f9490837e3a457cb763adc06054f65bb6772d0 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 11 Dec 2018 10:14:18 +0100 Subject: [PATCH 09/10] fix docs build --- .../src/common/resources/relationtype.resource.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js index f17aae0a6b..7f13a46d2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relationtype.resource.js @@ -35,7 +35,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#getRelationObjectTypes - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Gets a list of Umbraco object types which can be associated with a relation. @@ -54,7 +54,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#save - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Updates a relation type. @@ -74,7 +74,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#create - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Creates a new relation type. @@ -94,7 +94,7 @@ function relationTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { /** * @ngdoc method * @name umbraco.resources.relationTypeResource#deleteById - * @methodof umbraco.resources.relationTypeResource + * @methodOf umbraco.resources.relationTypeResource * * @description * Deletes a relation type with a given ID. From 0ae9d30373a06fbe030c41493cc00b90d3f48bfd Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 11 Dec 2018 09:43:47 +0000 Subject: [PATCH 10/10] Revert recent change to Forms dashboard back into XML config - now that we have a way to modify from Umbraco Forms component --- .../App_Plugins/UmbracoForms/package.manifest | 10 ---------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 - src/Umbraco.Web.UI/config/Dashboard.config | 11 +++++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest diff --git a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest b/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest deleted file mode 100644 index c7ed4a957a..0000000000 --- a/src/Umbraco.Web.UI/App_Plugins/UmbracoForms/package.manifest +++ /dev/null @@ -1,10 +0,0 @@ -{ - "dashboards": [ - { - "name": "Install Umbraco Forms", - "alias": "installUmbracoForms", - "view": "views/dashboard/forms/formsdashboardintro.html", - "sections": [ "forms" ] - } - ] -} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 130015ad53..5751e9155c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -231,7 +231,6 @@ - 404handlers.config diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config index 785fb61681..fec6ab34ae 100644 --- a/src/Umbraco.Web.UI/config/Dashboard.config +++ b/src/Umbraco.Web.UI/config/Dashboard.config @@ -22,6 +22,17 @@
+
+ + forms + + + + views/dashboard/forms/formsdashboardintro.html + + +
+
media