diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 6b0f92858d..99e72bae0f 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -8,6 +8,12 @@ $(DefaultItemExcludes);wwwroot/media/**; + + + + + + @@ -30,6 +36,10 @@ + + true + + false diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index 4045421eb1..1b7c6d46fc 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -47,6 +47,7 @@ namespace JsonSchema public RichTextEditorSettings RichTextEditor { get; set; } public RuntimeMinificationSettings RuntimeMinification { get; set; } public BasicAuthSettings BasicAuth { get; set; } + public PackageMigrationSettings PackageMigration { get; set; } } /// diff --git a/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs b/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs new file mode 100644 index 0000000000..27968fdcd2 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs @@ -0,0 +1,40 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.ComponentModel; + +namespace Umbraco.Cms.Core.Configuration.Models +{ + /// + /// Typed configuration options for package migration settings. + /// + [UmbracoOptions(Constants.Configuration.ConfigPackageMigration)] + public class PackageMigrationSettings + { + private const bool StaticRunSchemaAndContentMigrations = true; + private const bool StaticAllowComponentOverrideOfRunSchemaAndContentMigrations = true; + + /// + /// Gets or sets a value indicating whether package migration steps that install schema and content should run. + /// + /// + /// By default this is true and schema and content defined in a package migration are installed. + /// Using configuration, administrators can optionally switch this off in certain environments. + /// Deployment tools such as Umbraco Deploy can also configure this option to run or not run these migration + /// steps as is appropriate for normal use of the tool. + /// + [DefaultValue(StaticRunSchemaAndContentMigrations)] + public bool RunSchemaAndContentMigrations { get; set; } = StaticRunSchemaAndContentMigrations; + + /// + /// Gets or sets a value indicating whether components can override the configured value for . + /// + /// + /// By default this is true and components can override the configured setting for . + /// If an administrator wants explicit control over which environments migration steps installing schema and content can run, + /// they can set this to false. Components should respect this and not override the configuration. + /// + [DefaultValue(StaticAllowComponentOverrideOfRunSchemaAndContentMigrations)] + public bool AllowComponentOverrideOfRunSchemaAndContentMigrations { get; set; } = StaticAllowComponentOverrideOfRunSchemaAndContentMigrations; + } +} diff --git a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs index b03528fd0a..fe999f7bc0 100644 --- a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Configuration.Models { internal const bool StaticUseInMemoryCache = false; internal const string StaticCacheBuster = "Version"; + internal const string StaticVersion = null; /// /// Use in memory cache @@ -19,5 +20,11 @@ namespace Umbraco.Cms.Core.Configuration.Models /// [DefaultValue(StaticCacheBuster)] public RuntimeMinificationCacheBuster CacheBuster { get; set; } = Enum.Parse(StaticCacheBuster); + + /// + /// The unique version string used if CacheBuster is 'Version'. + /// + [DefaultValue(StaticVersion)] + public string Version { get; set; } = StaticVersion; } } diff --git a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs index 08020f6e89..7103a9534e 100644 --- a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs @@ -1,17 +1,19 @@ -using System.ComponentModel; +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Umbraco.Cms.Core.Configuration.Models { - /// /// Typed configuration options for unattended settings. /// [UmbracoOptions(Constants.Configuration.ConfigUnattended)] public class UnattendedSettings { - internal const bool StaticInstallUnattended = false; - internal const bool StaticUpgradeUnattended = false; + private const bool StaticInstallUnattended = false; + private const bool StaticUpgradeUnattended = false; /// /// Gets or sets a value indicating whether unattended installs are enabled. diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs index 0c7657d07e..c36f5813ab 100644 --- a/src/Umbraco.Core/Constants-Configuration.cs +++ b/src/Umbraco.Core/Constants-Configuration.cs @@ -52,6 +52,7 @@ public const string ConfigWebRouting = ConfigPrefix + "WebRouting"; public const string ConfigUserPassword = ConfigPrefix + "Security:UserPassword"; public const string ConfigRichTextEditor = ConfigPrefix + "RichTextEditor"; + public const string ConfigPackageMigration = ConfigPrefix + "PackageMigration"; } } } diff --git a/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs b/src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs similarity index 64% rename from src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs rename to src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs index c73685b41d..8d195c56f4 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs +++ b/src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs @@ -4,22 +4,22 @@ using System.ComponentModel; namespace Umbraco.Cms.Web.Common.DependencyInjection { /// - /// INTERNAL Service locator. Should only be used if no other ways exist. + /// Service locator for internal (umbraco cms) only purposes. Should only be used if no other ways exist. /// /// /// It is created with only two goals in mind /// 1) Continue to have the same extension methods on IPublishedContent and IPublishedElement as in V8. To make migration easier. - /// 2) To have a tool to avoid breaking changes in minor versions. All methods using this should in theory be obsolete. + /// 2) To have a tool to avoid breaking changes in minor and patch versions. All methods using this should in theory be obsolete. /// /// Keep in mind, every time this is used, the code becomes basically untestable. /// [EditorBrowsable(EditorBrowsableState.Never)] - internal static class StaticServiceProvider + public static class StaticServiceProvider { /// /// The service locator. /// [EditorBrowsable(EditorBrowsableState.Never)] - internal static IServiceProvider Instance { get; set; } + public static IServiceProvider Instance { get; set; } } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 77902cc5c1..9b31ed7056 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -72,7 +72,8 @@ namespace Umbraco.Cms.Core.DependencyInjection .AddUmbracoOptions() .AddUmbracoOptions() .AddUmbracoOptions() - .AddUmbracoOptions(); + .AddUmbracoOptions() + .AddUmbracoOptions(); return builder; } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 6f6a53df66..bf3b3edaf9 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -38,6 +38,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.DependencyInjection diff --git a/src/Umbraco.Core/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs index 2aa74474d1..42ac9f7ef0 100644 --- a/src/Umbraco.Core/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/InstallationSummary.cs @@ -32,6 +32,7 @@ namespace Umbraco.Cms.Core.Packaging public IEnumerable PartialViewsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable MediaInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable EntityContainersInstalled { get; set; } = Enumerable.Empty(); public override string ToString() { @@ -77,6 +78,7 @@ namespace Umbraco.Cms.Core.Packaging WriteCount("Stylesheets installed: ", StylesheetsInstalled); WriteCount("Scripts installed: ", ScriptsInstalled); WriteCount("Partial views installed: ", PartialViewsInstalled); + WriteCount("Entity containers installed: ", EntityContainersInstalled); WriteCount("Content items installed: ", ContentInstalled); WriteCount("Media items installed: ", MediaInstalled, false); diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index 548387a675..590262946d 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -237,7 +237,7 @@ namespace Umbraco.Cms.Core.Routing // re-route await RouteRequestInternalAsync(builder); - + // return if we are redirect if (builder.IsRedirect()) { @@ -252,6 +252,11 @@ namespace Umbraco.Cms.Core.Routing builder.SetPublishedContent(content); } + if (!builder.HasDomain()) + { + FindDomain(builder); + } + return BuildRequest(builder); } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs index c14d3e5119..fef61a54c3 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs @@ -1,8 +1,9 @@ using System; using System.Xml.Linq; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; @@ -20,7 +21,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) + IMigrationContext context, + IOptions options) : base(new ImportPackageBuilderExpression( packagingService, mediaService, @@ -28,7 +30,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, - context)) + context, + options)) { } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs index 8eda0f0b45..838d59e14e 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs @@ -5,7 +5,9 @@ using System.Linq; using System.Xml.Linq; using System.Xml.XPath; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Packaging; @@ -25,7 +27,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IPackagingService _packagingService; private readonly IShortStringHelper _shortStringHelper; - private bool _executed; + private readonly PackageMigrationSettings _packageMigrationSettings; + + private bool _executed; public ImportPackageBuilderExpression( IPackagingService packagingService, @@ -34,7 +38,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) : base(context) + IMigrationContext context, + IOptions packageMigrationSettings) : base(context) { _packagingService = packagingService; _mediaService = mediaService; @@ -42,6 +47,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _packageMigrationSettings = packageMigrationSettings.Value; } /// @@ -59,6 +65,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging } _executed = true; + Context.BuildingExpression = false; if (EmbeddedResourceMigrationType == null && PackageDataManifest == null) @@ -67,6 +74,12 @@ namespace Umbraco.Cms.Infrastructure.Packaging $"Nothing to execute, neither {nameof(EmbeddedResourceMigrationType)} or {nameof(PackageDataManifest)} has been set."); } + if (!_packageMigrationSettings.RunSchemaAndContentMigrations) + { + Logger.LogInformation("Skipping import of embedded schema file, due to configuration"); + return; + } + InstallationSummary installationSummary; if (EmbeddedResourceMigrationType != null) { diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index c691b74a0c..5c9942f945 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -91,19 +91,25 @@ namespace Umbraco.Cms.Infrastructure.Packaging var installationSummary = new InstallationSummary(compiledPackage.Name) { Warnings = compiledPackage.Warnings, - DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), + DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId, out IEnumerable dataTypeEntityContainersInstalled), LanguagesInstalled = ImportLanguages(compiledPackage.Languages, userId), DictionaryItemsInstalled = ImportDictionaryItems(compiledPackage.DictionaryItems, userId), MacrosInstalled = ImportMacros(compiledPackage.Macros, userId), MacroPartialViewsInstalled = ImportMacroPartialViews(compiledPackage.MacroPartialViews, userId), TemplatesInstalled = ImportTemplates(compiledPackage.Templates.ToList(), userId), - DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId), - MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId), + DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId, out IEnumerable documentTypeEntityContainersInstalled), + MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId, out IEnumerable mediaTypeEntityContainersInstalled), StylesheetsInstalled = ImportStylesheets(compiledPackage.Stylesheets, userId), ScriptsInstalled = ImportScripts(compiledPackage.Scripts, userId), PartialViewsInstalled = ImportPartialViews(compiledPackage.PartialViews, userId) }; + var entityContainersInstalled = new List(); + entityContainersInstalled.AddRange(dataTypeEntityContainersInstalled); + entityContainersInstalled.AddRange(documentTypeEntityContainersInstalled); + entityContainersInstalled.AddRange(mediaTypeEntityContainersInstalled); + installationSummary.EntityContainersInstalled = entityContainersInstalled; + // We need a reference to the imported doc types to continue var importedDocTypes = installationSummary.DocumentTypesInstalled.ToDictionary(x => x.Alias, x => x); var importedMediaTypes = installationSummary.MediaTypesInstalled.ToDictionary(x => x.Alias, x => x); @@ -116,6 +122,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging return installationSummary; } } + /// /// Imports and saves package xml as /// @@ -123,7 +130,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the User performing the operation. Default is zero (admin). /// An enumerable list of generated ContentTypes public IReadOnlyList ImportMediaTypes(IEnumerable docTypeElements, int userId) - => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _mediaTypeService); + => ImportMediaTypes(docTypeElements, userId, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportMediaTypes(IEnumerable docTypeElements, int userId, out IEnumerable entityContainersInstalled) + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _mediaTypeService, out entityContainersInstalled); #endregion @@ -408,7 +425,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging #region DocumentTypes public IReadOnlyList ImportDocumentType(XElement docTypeElement, int userId) - => ImportDocumentTypes(new[] { docTypeElement }, userId); + => ImportDocumentTypes(new[] { docTypeElement }, userId, out _); /// /// Imports and saves package xml as @@ -417,7 +434,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the User performing the operation. Default is zero (admin). /// An enumerable list of generated ContentTypes public IReadOnlyList ImportDocumentTypes(IEnumerable docTypeElements, int userId) - => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService); + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportDocumentTypes(IEnumerable docTypeElements, int userId, out IEnumerable entityContainersInstalled) + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService, out entityContainersInstalled); /// /// Imports and saves package xml as @@ -428,6 +455,18 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// An enumerable list of generated ContentTypes public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId, IContentTypeBaseService service) where T : class, IContentTypeComposition + => ImportDocumentTypes(unsortedDocumentTypes, importStructure, userId, service); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Boolean indicating whether or not to import the + /// Optional id of the User performing the operation. Default is zero (admin). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId, IContentTypeBaseService service, out IEnumerable entityContainersInstalled) + where T : class, IContentTypeComposition { var importedContentTypes = new Dictionary(); @@ -436,7 +475,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; - var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes, out entityContainersInstalled); if (isSingleDocTypeImport == false) { @@ -532,9 +571,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging return list; } - private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes, out IEnumerable entityContainersInstalled) { var importedFolders = new Dictionary(); + var trackEntityContainersInstalled = new List(); foreach (var documentType in unsortedDocumentTypes) { var foldersAttribute = documentType.Attribute("Folders"); @@ -578,8 +618,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } + var rootFolderId = tryCreateFolder.Result.Entity.Id; current = _contentTypeService.GetContainer(rootFolderId); + trackEntityContainersInstalled.Add(current); } importedFolders.Add(alias, current.Id); @@ -589,11 +631,13 @@ namespace Umbraco.Cms.Infrastructure.Packaging var folderName = WebUtility.UrlDecode(folders[i]); Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; current = CreateContentTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); + trackEntityContainersInstalled.Add(current); importedFolders[alias] = current.Id; } } } + entityContainersInstalled = trackEntityContainersInstalled; return importedFolders; } @@ -1012,10 +1056,20 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the user /// An enumerable list of generated DataTypeDefinitions public IReadOnlyList ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId) + => ImportDataTypes(dataTypeElements, userId, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the user + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated DataTypeDefinitions + public IReadOnlyList ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId, out IEnumerable entityContainersInstalled) { var dataTypes = new List(); - var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements, out entityContainersInstalled); foreach (var dataTypeElement in dataTypeElements) { @@ -1072,9 +1126,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging return dataTypes; } - private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements, out IEnumerable entityContainersInstalled) { var importedFolders = new Dictionary(); + var trackEntityContainersInstalled = new List(); foreach (var datatypeElement in datatypeElements) { var foldersAttribute = datatypeElement.Attribute("Folders"); @@ -1103,7 +1158,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } + current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + trackEntityContainersInstalled.Add(current); } importedFolders.Add(name, current.Id); @@ -1113,11 +1170,12 @@ namespace Umbraco.Cms.Infrastructure.Packaging var folderName = WebUtility.UrlDecode(folders[i]); Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; current = CreateDataTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); + trackEntityContainersInstalled.Add(current); importedFolders[name] = current.Id; } } } - + entityContainersInstalled = trackEntityContainersInstalled; return importedFolders; } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs index 3166cdbd4f..54b96955d4 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs @@ -1,12 +1,17 @@ +using System; +using System.ComponentModel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Infrastructure.Packaging { - public abstract class PackageMigrationBase : MigrationBase { private readonly IPackagingService _packagingService; @@ -15,6 +20,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IOptions _packageMigrationsSettings; public PackageMigrationBase( IPackagingService packagingService, @@ -23,7 +29,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) + IMigrationContext context, + IOptions packageMigrationsSettings) : base(context) { _packagingService = packagingService; @@ -32,6 +39,29 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _packageMigrationsSettings = packageMigrationsSettings; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use ctor with all params")] + public PackageMigrationBase( + IPackagingService packagingService, + IMediaService mediaService, + MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IMigrationContext context) + : this( + packagingService, + mediaService, + mediaFileManager, + mediaUrlGenerators, + shortStringHelper, + contentTypeBaseServiceProvider, + context, + StaticServiceProvider.Instance.GetRequiredService>()) + { } public IImportPackageBuilder ImportPackage => BeginBuild( @@ -42,7 +72,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, - Context)); + Context, + _packageMigrationsSettings)); } } diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs index 2e9fb6cebc..0b874a80c2 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs @@ -1,7 +1,5 @@ using System; -using System.Reflection; using NPoco; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Persistence { @@ -18,6 +16,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// So far, this is very manual. We don't try to be clever and figure out whether the /// columns exist already. We just ignore it. /// Beware, the application MUST restart when this class behavior changes. + /// You can override the GetColmunnInfo method to control which columns this includes /// internal class UmbracoPocoDataBuilder : PocoDataBuilder { @@ -28,19 +27,5 @@ namespace Umbraco.Cms.Infrastructure.Persistence { _upgrading = upgrading; } - - protected override ColumnInfo GetColumnInfo(MemberInfo mi, Type type) - { - var columnInfo = base.GetColumnInfo(mi, type); - - // TODO: Is this upgrade flag still relevant? It's a lot of hacking to just set this value - // including the interface method ConfigureForUpgrade for this one circumstance. - if (_upgrading) - { - if (type == typeof(UserDto) && mi.Name == "TourData") columnInfo.IgnoreColumn = true; - } - - return columnInfo; - } } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index ad35cbf30a..4ec87dfde7 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -1,9 +1,9 @@ using System; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; @@ -13,7 +13,9 @@ using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using ComponentCollection = Umbraco.Cms.Core.Composing.ComponentCollection; namespace Umbraco.Cms.Infrastructure.Runtime { @@ -27,6 +29,9 @@ namespace Umbraco.Cms.Infrastructure.Runtime private readonly IMainDom _mainDom; private readonly IUmbracoDatabaseFactory _databaseFactory; private readonly IEventAggregator _eventAggregator; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoVersion _umbracoVersion; + private readonly IServiceProvider _serviceProvider; private CancellationToken _cancellationToken; /// @@ -40,7 +45,10 @@ namespace Umbraco.Cms.Infrastructure.Runtime IProfilingLogger profilingLogger, IMainDom mainDom, IUmbracoDatabaseFactory databaseFactory, - IEventAggregator eventAggregator) + IEventAggregator eventAggregator, + IHostingEnvironment hostingEnvironment, + IUmbracoVersion umbracoVersion, + IServiceProvider serviceProvider) { State = state; _loggerFactory = loggerFactory; @@ -50,9 +58,42 @@ namespace Umbraco.Cms.Infrastructure.Runtime _mainDom = mainDom; _databaseFactory = databaseFactory; _eventAggregator = eventAggregator; + _hostingEnvironment = hostingEnvironment; + _umbracoVersion = umbracoVersion; + _serviceProvider = serviceProvider; _logger = _loggerFactory.CreateLogger(); } + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete] + public CoreRuntime( + ILoggerFactory loggerFactory, + IRuntimeState state, + ComponentCollection components, + IApplicationShutdownRegistry applicationShutdownRegistry, + IProfilingLogger profilingLogger, + IMainDom mainDom, + IUmbracoDatabaseFactory databaseFactory, + IEventAggregator eventAggregator, + IHostingEnvironment hostingEnvironment, + IUmbracoVersion umbracoVersion + ):this( + loggerFactory, + state, + components, + applicationShutdownRegistry, + profilingLogger, + mainDom, + databaseFactory, + eventAggregator, + hostingEnvironment, + umbracoVersion, + null + ) + { + + } + /// /// Gets the state of the Umbraco runtime. /// @@ -70,6 +111,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime { _cancellationToken = cancellationToken; StaticApplicationLogging.Initialize(_loggerFactory); + StaticServiceProvider.Instance = _serviceProvider; AppDomain.CurrentDomain.UnhandledException += (_, args) => { diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index a982ed1744..58f3622e67 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -279,6 +279,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers "memberTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllTypes()) }, + { + "memberTypeQueryApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllTypes()) + }, { "memberGroupApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllGroups()) diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 4af907bdfc..7c1f6f4187 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -182,6 +182,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// Returns all member types /// + [Obsolete("Use MemberTypeQueryController.GetAllTypes instead as it only requires AuthorizationPolicies.TreeAccessMembersOrMemberTypes and not both this and AuthorizationPolicies.TreeAccessMemberTypes")] [Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)] public IEnumerable GetAllTypes() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs new file mode 100644 index 0000000000..1d15a6448a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Authorization; +using Constants = Umbraco.Cms.Core.Constants; + +namespace Umbraco.Cms.Web.BackOffice.Controllers +{ + /// + /// An API controller used for dealing with member types + /// + [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)] + public class MemberTypeQueryController : BackOfficeNotificationsController + { + private readonly IMemberTypeService _memberTypeService; + private readonly IUmbracoMapper _umbracoMapper; + + + public MemberTypeQueryController( + IMemberTypeService memberTypeService, + IUmbracoMapper umbracoMapper) + { + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + } + + /// + /// Returns all member types + /// + public IEnumerable GetAllTypes() => + _memberTypeService.GetAll() + .Select(_umbracoMapper.Map); + + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 7e62e514f7..672495c7eb 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -557,7 +557,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // i.e. "Some Person" var toMailBoxAddress = new MailboxAddress(to.Name, to.Email); - var mailMessage = new EmailMessage(fromEmail, toMailBoxAddress.ToString(), emailSubject, emailBody, true); + var mailMessage = new EmailMessage(null /*use info from smtp settings*/, toMailBoxAddress.ToString(), emailSubject, emailBody, true); await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.UserInvite, true); } diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs deleted file mode 100644 index fa5adf7aeb..0000000000 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; - -namespace Umbraco.Cms.Web.Common.DependencyInjection -{ - /// - /// A registered to automatically capture application services - /// - internal class UmbracoApplicationServicesCapture : IStartupFilter - { - /// - public Action Configure(Action next) => - app => - { - StaticServiceProvider.Instance = app.ApplicationServices; - next(app); - }; - } -} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 62573cfc7b..6755159fc1 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -54,8 +54,14 @@ namespace Umbraco.Extensions .Configure(options => options.CacheFolder = builder.BuilderHostingEnvironment.MapPathContentRoot(imagingSettings.Cache.CacheFolder)) // We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed) .RemoveProcessor() + .RemoveProcessor() + .RemoveProcessor() + .RemoveProcessor() .AddProcessor() - .AddProcessor(); + .AddProcessor() + .AddProcessor() + .AddProcessor() + .AddProcessor(); builder.Services.AddTransient, ImageSharpConfigurationOptions>(); diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index ef98553ba2..2d584f198e 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -123,10 +123,6 @@ namespace Umbraco.Extensions config, profiler); - // adds the umbraco startup filter which will call UseUmbraco early on before - // other start filters are applied (depending on the ordering of IStartupFilters in DI). - services.AddTransient(); - return new UmbracoBuilder(services, config, typeLoader, loggerFactory, profiler, appCaches, tempHostingEnvironment); } diff --git a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs index 4817956ef8..cc07b6bd28 100644 --- a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs @@ -362,7 +362,7 @@ namespace Umbraco.Cms.Web.Common.Security { // Store the userId for use after two factor check var userId = await UserManager.GetUserIdAsync(user); - await Context.SignInAsync(IdentityConstants.TwoFactorUserIdScheme, StoreTwoFactorInfo(userId, loginProvider)); + await Context.SignInAsync(TwoFactorAuthenticationType, StoreTwoFactorInfo(userId, loginProvider)); return SignInResult.TwoFactorRequired; } } @@ -372,7 +372,7 @@ namespace Umbraco.Cms.Web.Common.Security await Context.SignOutAsync(ExternalAuthenticationType); } if (loginProvider == null) - { + { await SignInWithClaimsAsync(user, isPersistent, new Claim[] { new Claim("amr", "pwd") }); } else diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index fcd62febf4..537df5aab4 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -48,5 +48,4 @@ <_Parameter1>Umbraco.Tests.UnitTests - diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index bf02d9618e..e1d0fbe8ac 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -46,10 +46,10 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter, local return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( - "memberTypeApiBaseUrl", + "memberTypeQueryApiBaseUrl", "GetAllTypes")), 'Failed to retrieve data for member types id'); - }, + }, getById: function (id) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js index 1a2f0735ce..aa10d5bf2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js @@ -16,7 +16,7 @@ partialViewName = parentId + "/" + partialViewName; } - return "@Html.Partial(\"" + partialViewName + "\")"; + return "@await Html.PartialAsync(\"" + partialViewName + "\")"; } function getQuerySnippet(queryExpression) { diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js index 316cfa7c59..69da0ce786 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js @@ -26,28 +26,28 @@ describe('service: templateHelper', function () { it('should return the snippet for inserting a partial from the root', function () { var parentId = ""; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Footer")'; + var snippet = '@await Html.PartialAsync("Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a folder', function () { var parentId = "Folder"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder/Footer")'; + var snippet = '@await Html.PartialAsync("Folder/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a nested folder', function () { var parentId = "Folder/NestedFolder"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder/NestedFolder/Footer")'; + var snippet = '@await Html.PartialAsync("Folder/NestedFolder/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a folder with spaces in its name', function () { var parentId = "Folder with spaces"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder with spaces/Footer")'; + var snippet = '@await Html.PartialAsync("Folder with spaces/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); });