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