diff --git a/.gitignore b/.gitignore index e38c71eef8..f5249bb70b 100644 --- a/.gitignore +++ b/.gitignore @@ -207,6 +207,7 @@ src/Umbraco.Tests/TEMP/ src/Umbraco.Tests.UnitTests/umbraco/Data/TEMP/ /src/Umbraco.Web.UI.NetCore/appsettings.Local.json -src/Umbraco.Tests.Integration/DatabaseContextTests.sdf +src/Umbraco.Tests.Integration.SqlCe/umbraco/Data/TEMP/ +src/Umbraco.Tests.Integration.SqlCe/DatabaseContextTests.sdf -src/Umbraco.Web.UI.NetCore/umbraco/config/appsettings-schema.json +src/Umbraco.Web.UI.NetCore/umbraco/config/appsettings-schema.json diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index d95949d865..f82d90c1b2 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -123,7 +123,7 @@ stages: displayName: dotnet test inputs: command: test - projects: '**\Umbraco.Tests.Integration.csproj' + projects: '**\Umbraco.Tests.Integration*.csproj' - stage: Acceptance_Tests displayName: Acceptance Tests dependsOn: [] diff --git a/build/templates/UmbracoProject/Views/_ViewImports.cshtml b/build/templates/UmbracoProject/Views/_ViewImports.cshtml index 3aaf133335..25e6e66533 100644 --- a/build/templates/UmbracoProject/Views/_ViewImports.cshtml +++ b/build/templates/UmbracoProject/Views/_ViewImports.cshtml @@ -1,4 +1,6 @@ @using Umbraco.Web.UI.NetCore @using Umbraco.Extensions @using Umbraco.Web.PublishedModels +@using Umbraco.Cms.Core.Models.PublishedContent +@using Microsoft.AspNetCore.Html @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index 616d641751..4045421eb1 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -46,6 +46,7 @@ namespace JsonSchema public UnattendedSettings Unattended { get; set; } public RichTextEditorSettings RichTextEditor { get; set; } public RuntimeMinificationSettings RuntimeMinification { get; set; } + public BasicAuthSettings BasicAuth { get; set; } } /// diff --git a/src/JsonSchema/JsonSchema.csproj b/src/JsonSchema/JsonSchema.csproj index 25d4fa5498..3b64612b62 100644 --- a/src/JsonSchema/JsonSchema.csproj +++ b/src/JsonSchema/JsonSchema.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs index 74f30b81b6..d087af77d8 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -11,9 +11,10 @@ namespace Umbraco.Extensions /// /// The builder. /// A function creating a published snapshot service. - public static void SetPublishedSnapshotService(this IUmbracoBuilder builder, Func factory) + public static IUmbracoBuilder SetPublishedSnapshotService(this IUmbracoBuilder builder, Func factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -21,10 +22,11 @@ namespace Umbraco.Extensions /// /// The type of the published snapshot service. /// The builder. - public static void SetPublishedSnapshotService(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetPublishedSnapshotService(this IUmbracoBuilder builder) where T : class, IPublishedSnapshotService { builder.Services.AddUnique(); + return builder; } /// @@ -32,9 +34,10 @@ namespace Umbraco.Extensions /// /// The builder. /// A published snapshot service. - public static void SetPublishedSnapshotService(this IUmbracoBuilder builder, IPublishedSnapshotService service) + public static IUmbracoBuilder SetPublishedSnapshotService(this IUmbracoBuilder builder, IPublishedSnapshotService service) { builder.Services.AddUnique(service); + return builder; } } } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 4299328b5d..07a2eb72c9 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -146,6 +146,7 @@ namespace Umbraco.Cms.Core.Composing "HtmlDiff,", "ICSharpCode.", "Iesi.Collections,", // used by NHibernate + "JetBrains.Annotations,", "LightInject.", // DI "LightInject,", "Lucene.", diff --git a/src/Umbraco.Core/Configuration/ConfigConnectionString.cs b/src/Umbraco.Core/Configuration/ConfigConnectionString.cs index dab615da51..e88d1f4d01 100644 --- a/src/Umbraco.Core/Configuration/ConfigConnectionString.cs +++ b/src/Umbraco.Core/Configuration/ConfigConnectionString.cs @@ -17,48 +17,57 @@ namespace Umbraco.Cms.Core.Configuration public string ProviderName { get; } public string Name { get; } - private string ParseProvider(string connectionString) + private static bool IsSqlCe(DbConnectionStringBuilder builder) => (builder.TryGetValue("Data Source", out var ds) + || builder.TryGetValue("DataSource", out ds)) && + ds is string dataSource && + dataSource.EndsWith(".sdf"); + + private static bool IsSqlServer(DbConnectionStringBuilder builder) => + !string.IsNullOrEmpty(GetServer(builder)) && + ((builder.TryGetValue("Database", out var db) && db is string database && + !string.IsNullOrEmpty(database)) || + (builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName && + !string.IsNullOrEmpty(attachDbFileName)) || + (builder.TryGetValue("Initial Catalog", out var i) && i is string initialCatalog && + !string.IsNullOrEmpty(initialCatalog))); + + private static string GetServer(DbConnectionStringBuilder builder) + { + if(builder.TryGetValue("Server", out var s) && s is string server) + { + return server; + } + + if ((builder.TryGetValue("Data Source", out var ds) + || builder.TryGetValue("DataSource", out ds)) && ds is string dataSource) + { + return dataSource; + } + + return ""; + } + + private static string ParseProvider(string connectionString) { if (string.IsNullOrEmpty(connectionString)) { return null; } - var builder = new DbConnectionStringBuilder + var builder = new DbConnectionStringBuilder {ConnectionString = connectionString}; + if (IsSqlCe(builder)) { - ConnectionString = connectionString - }; - - if ( - (builder.TryGetValue("Data Source", out var ds) - || builder.TryGetValue("DataSource", out ds)) && ds is string dataSource) - { - if (dataSource.EndsWith(".sdf")) - { - return Umbraco.Cms.Core.Constants.DbProviderNames.SqlCe; - } + return Constants.DbProviderNames.SqlCe; } - if (builder.TryGetValue("Server", out var s) && s is string server && !string.IsNullOrEmpty(server)) + if (IsSqlServer(builder)) { - if (builder.TryGetValue("Database", out var db) && db is string database && !string.IsNullOrEmpty(database)) - { - return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer; - } - - if (builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName && !string.IsNullOrEmpty(attachDbFileName)) - { - return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer; - } - - if (builder.TryGetValue("Initial Catalog", out var i) && i is string initialCatalog && !string.IsNullOrEmpty(initialCatalog)) - { - return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer; - } + return Constants.DbProviderNames.SqlServer; } - throw new ArgumentException("Cannot determine provider name from connection string", nameof(connectionString)); + throw new ArgumentException("Cannot determine provider name from connection string", + nameof(connectionString)); } } } diff --git a/src/Umbraco.Core/Configuration/ContentDashboardSettings.cs b/src/Umbraco.Core/Configuration/ContentDashboardSettings.cs index 6840d974aa..7bef36dba4 100644 --- a/src/Umbraco.Core/Configuration/ContentDashboardSettings.cs +++ b/src/Umbraco.Core/Configuration/ContentDashboardSettings.cs @@ -1,10 +1,10 @@  -namespace Umbraco.Core.Dashboards +namespace Umbraco.Cms.Core.Configuration { public class ContentDashboardSettings { private const string DefaultContentDashboardPath = "cms"; - + /// /// Gets a value indicating whether the content dashboard should be available to all users. /// diff --git a/src/Umbraco.Core/Configuration/Models/BasicAuthSettings.cs b/src/Umbraco.Core/Configuration/Models/BasicAuthSettings.cs new file mode 100644 index 0000000000..054619d843 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/BasicAuthSettings.cs @@ -0,0 +1,27 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.ComponentModel; +using System.Net; + +namespace Umbraco.Cms.Core.Configuration.Models +{ + /// + /// Typed configuration options for basic authentication settings. + /// + [UmbracoOptions(Constants.Configuration.ConfigBasicAuth)] + public class BasicAuthSettings + { + private const bool StaticEnabled = false; + + /// + /// Gets or sets a value indicating whether to keep the user logged in. + /// + [DefaultValue(StaticEnabled)] + public bool Enabled { get; set; } = StaticEnabled; + + + public string[] AllowedIPs { get; set; } = Array.Empty(); + } +} diff --git a/src/Umbraco.Core/Configuration/Models/NuCacheSettings.cs b/src/Umbraco.Core/Configuration/Models/NuCacheSettings.cs index bd92b4ba3c..7767d1dbdc 100644 --- a/src/Umbraco.Core/Configuration/Models/NuCacheSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/NuCacheSettings.cs @@ -30,5 +30,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// [DefaultValue(StaticSqlPageSize)] public int SqlPageSize { get; set; } = StaticSqlPageSize; + + public bool UnPublishedContentCompression { get; set; } = false; } } diff --git a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs index 48e08d596a..7d4dd45fb8 100644 --- a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs +++ b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs @@ -15,6 +15,7 @@ namespace Umbraco.Cms.Core.Configuration.Models internal const bool StaticHideDisabledUsersInBackOffice = false; internal const bool StaticAllowPasswordReset = true; internal const string StaticAuthCookieName = "UMB_UCONTEXT"; + internal const string StaticAllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\"; /// /// Gets or sets a value indicating whether to keep the user logged in. @@ -50,6 +51,12 @@ namespace Umbraco.Cms.Core.Configuration.Models /// public bool UsernameIsEmail { get; set; } = true; + /// + /// Gets or sets the set of allowed characters for a username + /// + [DefaultValue(StaticAllowedUserNameCharacters)] + public string AllowedUserNameCharacters { get; set; } = StaticAllowedUserNameCharacters; + /// /// Gets or sets a value for the user password settings. /// diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs index d596d3feec..0c7657d07e 100644 --- a/src/Umbraco.Core/Constants-Configuration.cs +++ b/src/Umbraco.Core/Constants-Configuration.cs @@ -46,6 +46,7 @@ public const string ConfigRuntimeMinification = ConfigPrefix + "RuntimeMinification"; public const string ConfigRuntimeMinificationVersion = ConfigRuntimeMinification + ":Version"; public const string ConfigSecurity = ConfigPrefix + "Security"; + public const string ConfigBasicAuth = ConfigPrefix + "BasicAuth"; public const string ConfigTours = ConfigPrefix + "Tours"; public const string ConfigTypeFinder = ConfigPrefix + "TypeFinder"; public const string ConfigWebRouting = ConfigPrefix + "WebRouting"; diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index b5edfdf818..77902cc5c1 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -71,6 +71,7 @@ namespace Umbraco.Cms.Core.DependencyInjection .AddUmbracoOptions() .AddUmbracoOptions() .AddUmbracoOptions() + .AddUmbracoOptions() .AddUmbracoOptions(); return builder; diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentTypesByKeys.cs b/src/Umbraco.Core/Models/ContentEditing/ContentTypesByKeys.cs new file mode 100644 index 0000000000..a9651e4f7a --- /dev/null +++ b/src/Umbraco.Core/Models/ContentEditing/ContentTypesByKeys.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace Umbraco.Cms.Core.Models.ContentEditing +{ + /// + /// A model for retrieving multiple content types based on their keys. + /// + [DataContract(Name = "contentTypes", Namespace = "")] + public class ContentTypesByKeys + { + /// + /// ID of the parent of the content type. + /// + [DataMember(Name = "parentId")] + [Required] + public int ParentId { get; set; } + + /// + /// The id of every content type to get. + /// + [DataMember(Name = "contentTypeKeys")] + [Required] + public Guid[] ContentTypeKeys { get; set; } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs index f0973f3157..61f31a85c9 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs @@ -9,7 +9,12 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// public interface IPropertyCacheCompression - { - bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias); + {/// + /// Whether a property on the content is/should be compressed + /// + /// The content + /// The property to compress or not + /// Whether this content is the published version + bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias, bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs index 86bda9e799..a63029fc3d 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs @@ -4,6 +4,13 @@ namespace Umbraco.Cms.Core.PropertyEditors { public interface IPropertyCacheCompressionOptions { - bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor); + /// + /// Whether a property on the content is/should be compressed + /// + /// The content + /// The property to compress or not + /// The datatype of the property to compress or not + /// Whether this content is the published version + bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs index 7e01f6b1cb..f3adb6a05f 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs @@ -12,6 +12,14 @@ namespace Umbraco.Cms.Core.PropertyEditors public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath) { + // We can't get a media path from a null value + // The value will be null when uploading a brand new image, since we try to get the "old path" which doesn't exist yet. + if (value is null) + { + mediaPath = null; + return false; + } + foreach(IMediaUrlGenerator generator in this) { if (generator.TryGetMediaPath(propertyEditorAlias, value, out var mp)) diff --git a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs index f2020ecbca..7e91d8e3ee 100644 --- a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs @@ -7,6 +7,6 @@ namespace Umbraco.Cms.Core.PropertyEditors /// public sealed class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions { - public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor) => false; + public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published) => false; } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs index 5216e3158f..3d6c0adff6 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; -namespace Umbraco.Core.PropertyEditors +namespace Umbraco.Cms.Core.PropertyEditors { /// @@ -16,13 +16,13 @@ namespace Umbraco.Core.PropertyEditors private readonly IPropertyCacheCompressionOptions _compressionOptions; private readonly IReadOnlyDictionary _contentTypes; private readonly PropertyEditorCollection _propertyEditors; - private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias), bool> _isCompressedCache; + private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias, bool published), bool> _isCompressedCache; public PropertyCacheCompression( IPropertyCacheCompressionOptions compressionOptions, IReadOnlyDictionary contentTypes, PropertyEditorCollection propertyEditors, - ConcurrentDictionary<(int, string), bool> compressedStoragePropertyEditorCache) + ConcurrentDictionary<(int, string, bool), bool> compressedStoragePropertyEditorCache) { _compressionOptions = compressionOptions; _contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes)); @@ -30,9 +30,9 @@ namespace Umbraco.Core.PropertyEditors _isCompressedCache = compressedStoragePropertyEditorCache; } - public bool IsCompressed(IReadOnlyContentBase content, string alias) + public bool IsCompressed(IReadOnlyContentBase content, string alias, bool published) { - var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias), x => + var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias, published), x => { if (!_contentTypes.TryGetValue(x.contentTypeId, out var ct)) return false; @@ -44,7 +44,7 @@ namespace Umbraco.Core.PropertyEditors if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor)) return false; - return _compressionOptions.IsCompressed(content, propertyType, propertyEditor); + return _compressionOptions.IsCompressed(content, propertyType, propertyEditor, published); }); return compressedStorage; diff --git a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs new file mode 100644 index 0000000000..dc7f080acf --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs @@ -0,0 +1,20 @@ +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Compress large, non published text properties + /// + public class UnPublishedContentPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions + { + public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published) + { + if (!published && propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext) + { + //Only compress non published content that supports publishing and the property is text + return true; + } + return false; + } + } +} diff --git a/src/Umbraco.Core/Services/IBasicAuthService.cs b/src/Umbraco.Core/Services/IBasicAuthService.cs new file mode 100644 index 0000000000..84173a629a --- /dev/null +++ b/src/Umbraco.Core/Services/IBasicAuthService.cs @@ -0,0 +1,10 @@ +using System.Net; + +namespace Umbraco.Cms.Core.Services +{ + public interface IBasicAuthService + { + bool IsBasicAuthEnabled(); + bool IsIpAllowListed(IPAddress clientIpAddress); + } +} diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs index ebb09f62c2..e0d10668ea 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs @@ -34,17 +34,6 @@ namespace Umbraco.Extensions public static string Localize(this ILocalizedTextService manager, string area, string alias, string[] tokens) => manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, ConvertToDictionaryVars(tokens)); - /// - /// Localize using the current thread culture - /// - /// - /// - /// - /// - /// - public static string Localize(this ILocalizedTextService manager, string area, string alias, IDictionary tokens = null) - => manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, tokens); - /// /// Localize a key without any variables /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2a30778d80..f43ac533a7 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -17,7 +17,7 @@ - + @@ -59,4 +59,8 @@ + + + + diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index 5dd6d9bde6..7473ce1bab 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -30,11 +30,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - 3.5.4 - runtime; build; native; contentfiles; analyzers - all - all diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs index 05dba2cc0f..71ea85d80f 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs @@ -2,12 +2,8 @@ using System; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Infrastructure.Search; using Umbraco.Cms.Infrastructure.Sync; using Umbraco.Extensions; @@ -40,49 +36,67 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The type of the server registrar. /// The builder. - public static void SetServerRegistrar(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder) where T : class, IServerRoleAccessor - => builder.Services.AddUnique(); + { + builder.Services.AddUnique(); + return builder; + } /// /// Sets the server registrar. /// /// The builder. /// A function creating a server registrar. - public static void SetServerRegistrar(this IUmbracoBuilder builder, Func factory) - => builder.Services.AddUnique(factory); + public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder, Func factory) + { + builder.Services.AddUnique(factory); + return builder; + } /// /// Sets the server registrar. /// /// The builder. /// A server registrar. - public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar) - => builder.Services.AddUnique(registrar); + public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar) + { + builder.Services.AddUnique(registrar); + return builder; + } /// /// Sets the server messenger. /// /// The type of the server registrar. /// The builder. - public static void SetServerMessenger(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder) where T : class, IServerMessenger - => builder.Services.AddUnique(); + { + builder.Services.AddUnique(); + return builder; + } /// /// Sets the server messenger. /// /// The builder. /// A function creating a server messenger. - public static void SetServerMessenger(this IUmbracoBuilder builder, Func factory) - => builder.Services.AddUnique(factory); + public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder, Func factory) + { + builder.Services.AddUnique(factory); + return builder; + } /// /// Sets the server messenger. /// /// The builder. /// A server messenger. - public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar) - => builder.Services.AddUnique(registrar); + public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar) + { + builder.Services.AddUnique(registrar); + return builder; + } } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index 8b34289c9c..e535b399e4 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -40,6 +40,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs index cbbaa6a3e0..b311b1f0da 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.IO; @@ -21,10 +20,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The type of the factory. /// The builder. - public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder) where T : class, ICultureDictionaryFactory { builder.Services.AddUnique(); + return builder; } /// @@ -32,9 +32,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A function creating a culture dictionary factory. - public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func factory) + public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -42,9 +43,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A factory. - public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory) + public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -52,10 +54,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The type of the factory. /// The builder. - public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder) where T : class, IPublishedModelFactory { builder.Services.AddUnique(); + return builder; } /// @@ -63,9 +66,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A function creating a published content model factory. - public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func factory) + public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -73,9 +77,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A published content model factory. - public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory) + public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -83,10 +88,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The type of the short string helper. /// The builder. - public static void SetShortStringHelper(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder) where T : class, IShortStringHelper { builder.Services.AddUnique(); + return builder; } /// @@ -94,9 +100,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A function creating a short string helper. - public static void SetShortStringHelper(this IUmbracoBuilder builder, Func factory) + public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder, Func factory) { builder.Services.AddUnique(factory); + return builder; } /// @@ -104,9 +111,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// A builder. /// A short string helper. - public static void SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper) + public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper) { builder.Services.AddUnique(helper); + return builder; } /// @@ -114,19 +122,23 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// A builder. /// Factory method to create an IFileSystem implementation used in the MediaFileManager - public static void SetMediaFileSystem(this IUmbracoBuilder builder, - Func filesystemFactory) => builder.Services.AddUnique( - provider => - { - IFileSystem filesystem = filesystemFactory(provider); - // We need to use the Filesystems to create a shadow wrapper, - // because shadow wrapper requires the IsScoped delegate from the FileSystems. - // This is used by the scope provider when taking control of the filesystems. - FileSystems fileSystems = provider.GetRequiredService(); - IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media"); + public static IUmbracoBuilder SetMediaFileSystem(this IUmbracoBuilder builder, + Func filesystemFactory) + { + builder.Services.AddUnique( + provider => + { + IFileSystem filesystem = filesystemFactory(provider); + // We need to use the Filesystems to create a shadow wrapper, + // because shadow wrapper requires the IsScoped delegate from the FileSystems. + // This is used by the scope provider when taking control of the filesystems. + FileSystems fileSystems = provider.GetRequiredService(); + IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media"); - return provider.CreateInstance(shadow); - }); + return provider.CreateInstance(shadow); + }); + return builder; + } /// /// Register FileSystems with a method to configure the . @@ -135,7 +147,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// Method that configures the . /// Throws exception if is null. /// Throws exception if full path can't be resolved successfully. - public static void ConfigureFileSystems(this IUmbracoBuilder builder, + public static IUmbracoBuilder ConfigureFileSystems(this IUmbracoBuilder builder, Action configure) { if (configure == null) @@ -150,6 +162,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection configure(provider, fileSystems); return fileSystems; }); + return builder; } /// @@ -157,10 +170,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The type of the log viewer. /// The builder. - public static void SetLogViewer(this IUmbracoBuilder builder) + public static IUmbracoBuilder SetLogViewer(this IUmbracoBuilder builder) where T : class, ILogViewer { builder.Services.AddUnique(); + return builder; } /// @@ -168,19 +182,21 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// /// The builder. /// A function creating a log viewer. - public static void SetLogViewer(this IUmbracoBuilder builder, Func factory) + public static IUmbracoBuilder SetLogViewer(this IUmbracoBuilder builder, Func factory) { builder.Services.AddUnique(factory); + return builder; } /// /// Sets the log viewer. /// /// A builder. - /// A log viewer. - public static void SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer) + /// A log viewer. + public static IUmbracoBuilder SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer) { builder.Services.AddUnique(viewer); + return builder; } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs index 5f699d66d5..ae2098b75a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs @@ -9,6 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Expressions.Create.Table public class CreateTableOfDtoBuilder : IExecutableBuilder { private readonly IMigrationContext _context; + + // TODO: This doesn't do anything. private readonly DatabaseType[] _supportedDatabaseTypes; public CreateTableOfDtoBuilder(IMigrationContext context, params DatabaseType[] supportedDatabaseTypes) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index b31fb0d363..4eb9c5ae38 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -211,7 +211,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade Merge() .To("{8DDDCD0B-D7D5-4C97-BD6A-6B38CA65752F}") .To("{4695D0C9-0729-4976-985B-048D503665D8}") - + .To("{5C424554-A32D-4852-8ED1-A13508187901}") // to 9.0.0 RC .With() .To("{22D801BA-A1FF-4539-BFCC-2139B55594F8}") @@ -224,6 +224,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade .As("{5060F3D2-88BE-4D30-8755-CF51F28EAD12}"); // TO 9.0.0 + + + // This should be safe to execute again. We need it with a new name to ensure updates from all the following has executed this step. + // - 8.15 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8} + // - 8.15 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901} + // - 9.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12} + To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); + To("{10F7BB61-C550-426B-830B-7F954F689CDF}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs index 2f0caa4939..df453cad43 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs @@ -1,6 +1,9 @@ +using NPoco; using System.Linq; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0 { @@ -14,12 +17,49 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0 protected override void Migrate() { - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); + // allow null for the `data` field + if (DatabaseType.IsSqlCe()) + { + // SQLCE does not support altering NTEXT, so we have to jump through some hoops to do it + // All column ordering must remain the same as what is defined in the DTO so we need to create a temp table, + // drop orig and then re-create/copy. + Create.Table(withoutKeysAndIndexes: true).Do(); + Execute.Sql($"INSERT INTO [{TempTableName}] SELECT nodeId, published, data, rv FROM [{Constants.DatabaseSchema.Tables.NodeData}]").Do(); + Delete.Table(Constants.DatabaseSchema.Tables.NodeData).Do(); + Create.Table().Do(); + Execute.Sql($"INSERT INTO [{Constants.DatabaseSchema.Tables.NodeData}] SELECT nodeId, published, data, rv, NULL FROM [{TempTableName}]").Do(); + } + else + { + AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); + } + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); AddColumnIfNotExists(columns, "dataRaw"); // allow null - AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); + AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); + } + + private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP"; + + [TableName(TempTableName)] + [ExplicitColumns] + private class ContentNuDtoTemp + { + [Column("nodeId")] + public int NodeId { get; set; } + + [Column("published")] + public bool Published { get; set; } + + [Column("data")] + [SpecialDbType(SpecialDbTypes.NTEXT)] + [NullSetting(NullSetting = NullSettings.Null)] + public string Data { get; set; } + + [Column("rv")] + public long Rv { get; set; } } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs new file mode 100644 index 0000000000..868343374d --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs @@ -0,0 +1,19 @@ +using Umbraco.Cms.Infrastructure.Persistence; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0 +{ + public class UpdateCmsPropertyGroupIdSeed : MigrationBase + { + public UpdateCmsPropertyGroupIdSeed(IMigrationContext context) : base(context) + { + } + + protected override void Migrate() + { + if (DatabaseType.IsSqlCe()) + { + Database.Execute(Sql("ALTER TABLE [cmsPropertyTypeGroup] ALTER COLUMN [id] IDENTITY (56,1)")); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs b/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs index 14cc8e31f8..9e5101550a 100644 --- a/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs +++ b/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; -namespace Umbraco.Core.Models +namespace Umbraco.Cms.Core.Models { /// /// Represents a media item with local crops. diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs index 9084cc902f..a5186963ab 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs @@ -98,6 +98,13 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder.Embedded\", \"{1}\")]\n", tabs, ApiVersion.Current.Version); } + // writes an attribute that specifies that an output may be null. + // (useful for consuming projects with nullable reference types enabled) + private static void WriteMaybeNullAttribute(StringBuilder sb, string tabs, bool isReturn = false) + { + sb.AppendFormat("{0}[{1}global::System.Diagnostics.CodeAnalysis.MaybeNull]\n", tabs, isReturn ? "return: " : ""); + } + private void WriteContentType(StringBuilder sb, TypeModel type) { string sep; @@ -185,9 +192,11 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building sb.AppendFormat("\t\tpublic new const PublishedItemType ModelItemType = PublishedItemType.{0};\n", itemType); WriteGeneratedCodeAttribute(sb, "\t\t"); + WriteMaybeNullAttribute(sb, "\t\t", true); sb.Append("\t\tpublic new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor)\n"); sb.Append("\t\t\t=> PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias);\n"); WriteGeneratedCodeAttribute(sb, "\t\t"); + WriteMaybeNullAttribute(sb, "\t\t", true); sb.AppendFormat("\t\tpublic static IPublishedPropertyType GetModelPropertyType(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression> selector)\n", type.ClrName); sb.Append("\t\t\t=> PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector);\n"); @@ -305,6 +314,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building } WriteGeneratedCodeAttribute(sb, "\t\t"); + if (!property.ModelClrType.IsValueType) + WriteMaybeNullAttribute(sb, "\t\t"); sb.AppendFormat("\t\t[ImplementPropertyType(\"{0}\")]\n", property.Alias); if (mixinStatic) @@ -349,6 +360,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building sb.AppendFormat("\t\t/// Static getter for {0}\n", XmlCommentString(property.Name)); WriteGeneratedCodeAttribute(sb, "\t\t"); + if (!property.ModelClrType.IsValueType) + WriteMaybeNullAttribute(sb, "\t\t", true); sb.Append("\t\tpublic static "); WriteClrType(sb, property.ClrTypeName); sb.AppendFormat(" {0}(I{1} that, IPublishedValueFallback publishedValueFallback) => that.Value", @@ -404,6 +417,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building if (!string.IsNullOrWhiteSpace(property.Name)) sb.AppendFormat("\t\t/// {0}\n", XmlCommentString(property.Name)); WriteGeneratedCodeAttribute(sb, "\t\t"); + if (!property.ModelClrType.IsValueType) + WriteMaybeNullAttribute(sb, "\t\t"); + sb.Append("\t\t"); WriteClrType(sb, property.ClrTypeName); sb.AppendFormat(" {0} {{ get; }}\n", diff --git a/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs b/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs index 4ce6abe7a0..a5524b44de 100644 --- a/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/BasicBulkSqlInsertProvider.cs @@ -13,10 +13,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence public int BulkInsertRecords(IUmbracoDatabase database, IEnumerable records) { - var recordsA = records.ToArray(); - if (recordsA.Length == 0) return 0; + if (!records.Any()) return 0; - return BulkInsertRecordsWithCommands(database, recordsA); + return BulkInsertRecordsWithCommands(database, records.ToArray()); } /// diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupDto.cs index 5f3b2a9572..1fbc1b734c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupDto.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos internal class PropertyTypeGroupDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 12)] + [PrimaryKeyColumn(IdentitySeed = 56)] public int Id { get; set; } [Column("contenttypeNodeId")] diff --git a/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs b/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs index 71e249a836..c3875d3770 100644 --- a/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs +++ b/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs @@ -41,9 +41,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence _tableDefinition = DefinitionFactory.GetTableDefinition(pd.Type, sqlSyntaxProvider); if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.Type); - // only real columns, exclude result columns + // only real columns, exclude result/computed columns + // Like NPoco does: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L59 _readerColumns = pd.Columns - .Where(x => x.Value.ResultColumn == false) + .Where(x => x.Value.ResultColumn == false && x.Value.ComputedColumn == false) .Select(x => x.Value) .ToArray(); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs index 8b80010335..d67c97f2c4 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlServerBulkSqlInsertProvider.cs @@ -39,6 +39,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// The number of records that were inserted. private int BulkInsertRecordsSqlServer(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) { + // TODO: The main reason this exists is because the NPoco InsertBulk method doesn't return the number of items. + // It is worth investigating the performance of this vs NPoco's because we use a custom BulkDataReader + // which in theory should be more efficient than NPocos way of building up an in-memory DataTable. + // create command against the original database.Connection using (var command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty)) { @@ -50,7 +54,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence var syntax = database.SqlContext.SqlSyntax as SqlServerSyntaxProvider; if (syntax == null) throw new NotSupportedException("SqlSyntax must be SqlServerSyntaxProvider."); - using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) { BulkCopyTimeout = 10000, DestinationTableName = tableName }) + using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) + { + BulkCopyTimeout = 0, // 0 = no bulk copy timeout. If a timeout occurs it will be an connection/command timeout. + DestinationTableName = tableName, + // be consistent with NPoco: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L50 + BatchSize = 4096 + }) using (var bulkReader = new PocoDataDataReader(records, pocoData, syntax)) { //we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs index 51bf3c477c..ae1290d6a4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; -using Umbraco.Core.Models; +using Umbraco.Cms.Core.Models; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters diff --git a/src/Umbraco.Infrastructure/Services/Implement/BasicAuthService.cs b/src/Umbraco.Infrastructure/Services/Implement/BasicAuthService.cs new file mode 100644 index 0000000000..9e413b7162 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Implement/BasicAuthService.cs @@ -0,0 +1,33 @@ +using System.Net; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.Cms.Core.Services.Implement +{ + public class BasicAuthService : IBasicAuthService + { + private BasicAuthSettings _basicAuthSettings; + + public BasicAuthService(IOptionsMonitor optionsMonitor) + { + _basicAuthSettings = optionsMonitor.CurrentValue; + + optionsMonitor.OnChange(basicAuthSettings => _basicAuthSettings = basicAuthSettings); + } + + public bool IsBasicAuthEnabled() => _basicAuthSettings.Enabled; + + public bool IsIpAllowListed(IPAddress clientIpAddress) + { + foreach (var allowedIpString in _basicAuthSettings.AllowedIPs) + { + if (IPNetwork.TryParse(allowedIpString, out IPNetwork allowedIp) && allowedIp.Contains(clientIpAddress)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 712323656d..7a7bc628b8 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -19,15 +19,16 @@ - + + - + - + @@ -42,8 +43,8 @@ - - + + diff --git a/src/Umbraco.Persistence.SqlCe/SqlCeBulkSqlInsertProvider.cs b/src/Umbraco.Persistence.SqlCe/SqlCeBulkSqlInsertProvider.cs index 667c5262d5..e6ed41548d 100644 --- a/src/Umbraco.Persistence.SqlCe/SqlCeBulkSqlInsertProvider.cs +++ b/src/Umbraco.Persistence.SqlCe/SqlCeBulkSqlInsertProvider.cs @@ -17,13 +17,12 @@ namespace Umbraco.Cms.Persistence.SqlCe public int BulkInsertRecords(IUmbracoDatabase database, IEnumerable records) { - var recordsA = records.ToArray(); - if (recordsA.Length == 0) return 0; + if (!records.Any()) return 0; var pocoData = database.PocoDataFactory.ForType(typeof(T)); if (pocoData == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T)); - return BulkInsertRecordsSqlCe(database, pocoData, recordsA); + return BulkInsertRecordsSqlCe(database, pocoData, records.ToArray()); } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs index 59b73c21b0..99ce9365fc 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using CSharpTest.Net.Serialization; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// Serializes/Deserializes data to BTree data source for /// - internal class ContentDataSerializer : ISerializer + public class ContentDataSerializer : ISerializer { public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPropertyDataSerializer = null) { diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs index 1b8089d8ba..c813e428d2 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs @@ -1,11 +1,11 @@ -using System.Configuration; +using System.Configuration; using CSharpTest.Net.Collections; using CSharpTest.Net.Serialization; using Umbraco.Cms.Core.Configuration.Models; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { - internal class BTree + public class BTree { public static BPlusTree GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer contentDataSerializer = null) { diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs index 65412f10d2..cbc40a9630 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs @@ -14,12 +14,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// Deserialize the data into a /// - ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData); + ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published); /// - /// Serializes the + /// Serializes the /// - ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model); + ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published); } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs index bffa66898d..702689a995 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs @@ -3,7 +3,7 @@ using System.IO; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { - internal interface IDictionaryOfPropertyDataSerializer + public interface IDictionaryOfPropertyDataSerializer { IDictionary ReadFrom(Stream stream); void WriteTo(IDictionary value, Stream stream); diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs index 8fc953f353..9f1bfd39e2 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource DateFormatString = "o" }; private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable(); - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) { if (stringData == null && byteData != null) throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { // note that numeric values (which are Int32) are serialized without their // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs index 66c01cf1dc..7caf6eac7d 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs @@ -39,7 +39,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource _options = defaultOptions .WithResolver(resolver) - .WithCompression(MessagePackCompression.Lz4BlockArray); + .WithCompression(MessagePackCompression.Lz4BlockArray) + .WithSecurity(MessagePackSecurity.UntrustedData); } public string ToJson(byte[] bin) @@ -48,12 +49,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource return json; } - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) { if (byteData != null) { var cacheModel = MessagePackSerializer.Deserialize(byteData, _options); - Expand(content, cacheModel); + Expand(content, cacheModel, published); return cacheModel; } else if (stringData != null) @@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource // NOTE: We don't really support strings but it's possible if manually used (i.e. tests) var bin = Convert.FromBase64String(stringData); var cacheModel = MessagePackSerializer.Deserialize(bin, _options); - Expand(content, cacheModel); + Expand(content, cacheModel, published); return cacheModel; } else @@ -70,9 +71,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { - Compress(content, model); + Compress(content, model, published); var bytes = MessagePackSerializer.Serialize(model, _options); return new ContentCacheDataSerializationResult(null, bytes); } @@ -80,7 +81,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// Used during serialization to compress properties /// + /// /// + /// /// /// This will essentially 'double compress' property data. The MsgPack data as a whole will already be compressed /// but this will go a step further and double compress property data so that it is stored in the nucache file @@ -88,11 +91,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant /// memory savings but could also affect performance of first rendering pages while decompression occurs. /// - private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model) + private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { foreach(var propertyAliasToData in model.PropertyData) { - if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published)) { foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { @@ -105,12 +108,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// /// Used during deserialization to map the property data as lazy or expand the value /// + /// /// - private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData) + /// + private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published) { foreach (var propertyAliasToData in nestedData.PropertyData) { - if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published)) { foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null)) { diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs index f93cd71ad2..732d51b3b1 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; -using Umbraco.Core.PropertyEditors; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource { @@ -14,7 +13,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource private readonly IMemberTypeService _memberTypeService; private readonly PropertyEditorCollection _propertyEditors; private readonly IPropertyCacheCompressionOptions _compressionOptions; - private readonly ConcurrentDictionary<(int, string), bool> _isCompressedCache = new ConcurrentDictionary<(int, string), bool>(); + private readonly ConcurrentDictionary<(int, string, bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string, bool), bool>(); public MsgPackContentNestedDataSerializerFactory( IContentTypeService contentTypeService, diff --git a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs index 7ef655b2a8..c52c1271ad 100644 --- a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs @@ -68,7 +68,20 @@ namespace Umbraco.Extensions throw new IndexOutOfRangeException(); } }); - builder.Services.AddSingleton(); + + builder.Services.AddSingleton(s => + { + IOptions options = s.GetRequiredService>(); + + if (options.Value.NuCacheSerializerType == NuCacheSerializerType.MessagePack && + options.Value.UnPublishedContentCompression) + { + return new UnPublishedContentPropertyCacheCompressionOptions(); + } + + return new NoopPropertyCacheCompressionOptions(); + }); + builder.Services.AddSingleton(s => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer())); // add the NuCache health check (hidden from type finder) diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs index a141872957..2919edb8c3 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs @@ -415,7 +415,7 @@ AND cmsContentNu.nodeId IS NULL UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders) }; - var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData); + var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData, published); var dto = new ContentNuDto { @@ -817,12 +817,13 @@ AND cmsContentNu.nodeId IS NULL } else { - var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); + bool published = false; + var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); d = new ContentData { Name = dto.EditName, - Published = false, + Published = published, TemplateId = dto.EditTemplateId, VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, @@ -847,13 +848,14 @@ AND cmsContentNu.nodeId IS NULL } else { - var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw); + bool published = true; + var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published); p = new ContentData { Name = dto.PubName, UrlSegment = deserializedContent.UrlSegment, - Published = true, + Published = published, TemplateId = dto.PubTemplateId, VersionId = dto.VersionId, VersionDate = dto.PubVersionDate, @@ -883,12 +885,13 @@ AND cmsContentNu.nodeId IS NULL if (dto.EditData == null && dto.EditDataRaw == null) throw new InvalidOperationException("No data for media " + dto.Id); - var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); + bool published = true; + var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); var p = new ContentData { Name = dto.EditName, - Published = true, + Published = published, TemplateId = -1, VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index bedc6b7137..7d6ff54c25 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -19,8 +19,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all diff --git a/src/Umbraco.TestData/Configuration/TestDataSettings.cs b/src/Umbraco.TestData/Configuration/TestDataSettings.cs new file mode 100644 index 0000000000..78084f726a --- /dev/null +++ b/src/Umbraco.TestData/Configuration/TestDataSettings.cs @@ -0,0 +1,16 @@ +namespace Umbraco.TestData.Configuration +{ + public class TestDataSettings + { + /// + /// Gets or sets a value indicating whether the test data generation is enabled. + /// + public bool Enabled { get; set; } = false; + + /// + /// Gets or sets a value indicating whether persisted local database cache files for content and media are disabled. + /// + /// The URL path. + public bool IgnoreLocalDb { get; set; } = false; + } +} diff --git a/src/Umbraco.TestData/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.TestData/Extensions/UmbracoBuilderExtensions.cs new file mode 100644 index 0000000000..7e3e2a70b1 --- /dev/null +++ b/src/Umbraco.TestData/Extensions/UmbracoBuilderExtensions.cs @@ -0,0 +1,54 @@ +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Web.Common.ApplicationBuilder; +using Umbraco.TestData.Configuration; + +namespace Umbraco.TestData.Extensions +{ + public static class UmbracoBuilderExtensions + { + public static IUmbracoBuilder AddUmbracoTestData(this IUmbracoBuilder builder) + { + if (builder.Services.Any(x => x.ServiceType == typeof(LoadTestController))) + { + // We assume the test data project is composed if any implementations of LoadTestController exist. + return builder; + } + + IConfigurationSection testDataSection = builder.Config.GetSection("Umbraco:CMS:TestData"); + TestDataSettings config = testDataSection.Get(); + if (config == null || config.Enabled == false) + { + return builder; + } + + builder.Services.Configure(testDataSection); + + if (config.IgnoreLocalDb) + { + builder.Services.AddSingleton(factory => new PublishedSnapshotServiceOptions + { + IgnoreLocalDb = true + }); + } + + builder.Services.Configure(options => + options.AddFilter(new UmbracoPipelineFilter(nameof(LoadTestController)) + { + Endpoints = app => app.UseEndpoints(endpoints => + endpoints.MapControllerRoute( + "LoadTest", + "/LoadTest/{action}", + new { controller = "LoadTest", Action = "Index" })) + })); + + builder.Services.AddScoped(typeof(LoadTestController)); + + return builder; + } + } +} diff --git a/src/Umbraco.TestData/LoadTestComponent.cs b/src/Umbraco.TestData/LoadTestComponent.cs deleted file mode 100644 index cfd923cd07..0000000000 --- a/src/Umbraco.TestData/LoadTestComponent.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Web.Mvc; -using System.Web.Routing; -using System.Configuration; -using Umbraco.Cms.Core.Composing; - -// see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting - -namespace Umbraco.TestData -{ - public class LoadTestComponent : IComponent - { - public void Initialize() - { - if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") - return; - - - - RouteTable.Routes.MapRoute( - name: "LoadTest", - url: "LoadTest/{action}", - defaults: new - { - controller = "LoadTest", - action = "Index" - }, - namespaces: new[] { "Umbraco.TestData" } - ); - } - - public void Terminate() - { - } - } -} diff --git a/src/Umbraco.TestData/LoadTestComposer.cs b/src/Umbraco.TestData/LoadTestComposer.cs index e5b16e5ab1..8d66c4965d 100644 --- a/src/Umbraco.TestData/LoadTestComposer.cs +++ b/src/Umbraco.TestData/LoadTestComposer.cs @@ -1,31 +1,13 @@ -using System.Configuration; -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.TestData.Extensions; // see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting namespace Umbraco.TestData { - public class LoadTestComposer : ComponentComposer, IUserComposer + public class LoadTestComposer : IUserComposer { - public override void Compose(IUmbracoBuilder builder) - { - base.Compose(builder); - - if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") - return; - - builder.Services.AddScoped(typeof(LoadTestController), typeof(LoadTestController)); - - if (ConfigurationManager.AppSettings["Umbraco.TestData.IgnoreLocalDb"] == "true") - { - builder.Services.AddSingleton(factory => new PublishedSnapshotServiceOptions - { - IgnoreLocalDb = true - }); - } - } + public void Compose(IUmbracoBuilder builder) => builder.AddUmbracoTestData(); } } diff --git a/src/Umbraco.TestData/LoadTestController.cs b/src/Umbraco.TestData/LoadTestController.cs index e1494fbdab..3033d2febb 100644 --- a/src/Umbraco.TestData/LoadTestController.cs +++ b/src/Umbraco.TestData/LoadTestController.cs @@ -1,21 +1,15 @@ -using System; -using System.Configuration; +using System; using System.Diagnostics; +using System.IO; using System.Linq; +using System.Text; using System.Threading; -using System.Web; -using System.Web.Hosting; -using System.Web.Mvc; -using System.Web.Routing; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using System.IO; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Hosting; // see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting @@ -23,27 +17,17 @@ namespace Umbraco.TestData { public class LoadTestController : Controller { - public LoadTestController( - ServiceContext serviceContext, - IShortStringHelper shortStringHelper, - IHostingEnvironment hostingEnvironment) - { - _serviceContext = serviceContext; - _shortStringHelper = shortStringHelper; - _hostingEnvironment = hostingEnvironment; - } + private static readonly Random s_random = new Random(); + private static readonly object s_locko = new object(); - private static readonly Random _random = new Random(); - private static readonly object _locko = new object(); + private static volatile int s_containerId = -1; - private static volatile int _containerId = -1; + private const string ContainerAlias = "LoadTestContainer"; + private const string ContentAlias = "LoadTestContent"; + private const int TextboxDefinitionId = -88; + private const int MaxCreate = 1000; - private const string _containerAlias = "LoadTestContainer"; - private const string _contentAlias = "LoadTestContent"; - private const int _textboxDefinitionId = -88; - private const int _maxCreate = 1000; - - private static readonly string HeadHtml = @" + private static readonly string s_headHtml = @" LoadTest