diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index c09dc7b9f6..3979720f9b 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -1,4 +1,5 @@ using System.Configuration; +using Umbraco.Configuration; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -10,6 +11,7 @@ namespace Umbraco.Core.Configuration public IHostingSettings HostingSettings { get; } = new HostingSettings(); public ICoreDebug CoreDebug { get; } = new CoreDebug(); + public IMachineKeyConfig MachineKeyConfig { get; } = new MachineKeyConfig(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -27,6 +29,7 @@ namespace Umbraco.Core.Configuration configs.AddPasswordConfigurations(); configs.Add(() => CoreDebug); + configs.Add(() => MachineKeyConfig); configs.Add(() => new ConnectionStrings(ioHelper)); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/MachineKeyConfig.cs b/src/Umbraco.Configuration/MachineKeyConfig.cs new file mode 100644 index 0000000000..4e3401e015 --- /dev/null +++ b/src/Umbraco.Configuration/MachineKeyConfig.cs @@ -0,0 +1,20 @@ +using System.Configuration; +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration +{ + public class MachineKeyConfig : IMachineKeyConfig + { + //TODO all the machineKey stuff should be replaced: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/replacing-machinekey?view=aspnetcore-3.1 + + public bool HasMachineKey + { + get + { + var machineKeySection = + ConfigurationManager.GetSection("system.web/machineKey") as ConfigurationSection; + return !(machineKeySection?.ElementInformation?.Source is null); + } + } + } +} diff --git a/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs b/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs new file mode 100644 index 0000000000..35969e668a --- /dev/null +++ b/src/Umbraco.Core/Configuration/IMachineKeyConfig.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Configuration +{ + public interface IMachineKeyConfig + { + bool HasMachineKey { get;} + } +} diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ContentDashboard.cs b/src/Umbraco.Core/Dashboards/ContentDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ContentDashboard.cs rename to src/Umbraco.Core/Dashboards/ContentDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ExamineDashboard.cs b/src/Umbraco.Core/Dashboards/ExamineDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ExamineDashboard.cs rename to src/Umbraco.Core/Dashboards/ExamineDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/FormsDashboard.cs b/src/Umbraco.Core/Dashboards/FormsDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/FormsDashboard.cs rename to src/Umbraco.Core/Dashboards/FormsDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/HealthCheckDashboard.cs b/src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/HealthCheckDashboard.cs rename to src/Umbraco.Core/Dashboards/HealthCheckDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/MediaDashboard.cs b/src/Umbraco.Core/Dashboards/MediaDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/MediaDashboard.cs rename to src/Umbraco.Core/Dashboards/MediaDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/MembersDashboard.cs b/src/Umbraco.Core/Dashboards/MembersDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/MembersDashboard.cs rename to src/Umbraco.Core/Dashboards/MembersDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/ProfilerDashboard.cs b/src/Umbraco.Core/Dashboards/ProfilerDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/ProfilerDashboard.cs rename to src/Umbraco.Core/Dashboards/ProfilerDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/PublishedStatusDashboard.cs b/src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/PublishedStatusDashboard.cs rename to src/Umbraco.Core/Dashboards/PublishedStatusDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/RedirectUrlDashboard.cs b/src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/RedirectUrlDashboard.cs rename to src/Umbraco.Core/Dashboards/RedirectUrlDashboard.cs diff --git a/src/Umbraco.Web.BackOffice/Dashboards/SettingsDashboards.cs b/src/Umbraco.Core/Dashboards/SettingsDashboards.cs similarity index 100% rename from src/Umbraco.Web.BackOffice/Dashboards/SettingsDashboards.cs rename to src/Umbraco.Core/Dashboards/SettingsDashboards.cs diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index 1662879cf2..41560292c7 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -22,11 +22,6 @@ namespace Umbraco.Core.Hosting string MapPath(string path); string ToAbsolute(string virtualPath, string root); - /// - /// Terminates the current application. The application restarts the next time a request is received for it. - /// - void LazyRestartApplication(); - void RegisterObject(IRegisteredObject registeredObject); void UnregisterObject(IRegisteredObject registeredObject); } diff --git a/src/Umbraco.Core/Manifest/ManifestWatcher.cs b/src/Umbraco.Core/Manifest/ManifestWatcher.cs index 8d81ac3de5..23caac3a1b 100644 --- a/src/Umbraco.Core/Manifest/ManifestWatcher.cs +++ b/src/Umbraco.Core/Manifest/ManifestWatcher.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Net; namespace Umbraco.Core.Manifest { @@ -13,13 +14,13 @@ namespace Umbraco.Core.Manifest private static volatile bool _isRestarting; private readonly ILogger _logger; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly List _fws = new List(); - public ManifestWatcher(ILogger logger, IHostingEnvironment hostingEnvironment) + public ManifestWatcher(ILogger logger, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _hostingEnvironment = hostingEnvironment; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } public void Start(params string[] packageFolders) @@ -57,7 +58,7 @@ namespace Umbraco.Core.Manifest _isRestarting = true; _logger.Info("Manifest has changed, app pool is restarting ({Path})", e.FullPath); - _hostingEnvironment.LazyRestartApplication(); + _umbracoApplicationLifetime.Restart(); Dispose(); // uh? if the app restarts then this should be disposed anyways? } } diff --git a/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs new file mode 100644 index 0000000000..10d5b10955 --- /dev/null +++ b/src/Umbraco.Core/Net/IUmbracoApplicationLifetime.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Net +{ + public interface IUmbracoApplicationLifetime + { + /// + /// A value indicating whether the application is restarting after the current request. + /// + bool IsRestarting { get; } + /// + /// Terminates the current application. The application restarts the next time a request is received for it. + /// + void Restart(); + } +} diff --git a/src/Umbraco.Core/Net/IUserAgentProvider.cs b/src/Umbraco.Core/Net/IUserAgentProvider.cs new file mode 100644 index 0000000000..14246ea99e --- /dev/null +++ b/src/Umbraco.Core/Net/IUserAgentProvider.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Net +{ + public interface IUserAgentProvider + { + string GetUserAgent(); + } +} diff --git a/src/Umbraco.Core/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Packaging/PackageDefinition.cs index 11bf26c579..29a1919a2b 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinition.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Packaging /// /// This is used only for conversions and will not 'get' a PackageDefinition from the repository with a valid ID /// - internal static PackageDefinition FromCompiledPackage(CompiledPackage compiled) + public static PackageDefinition FromCompiledPackage(CompiledPackage compiled) { return new PackageDefinition { diff --git a/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs b/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs new file mode 100644 index 0000000000..74e2dd7380 --- /dev/null +++ b/src/Umbraco.Core/UmbracoContextAccessorExtensions.cs @@ -0,0 +1,19 @@ +using System; +using Umbraco.Web; + +namespace Umbraco.Core +{ + public static class UmbracoContextAccessorExtensions + { + public static IUmbracoContext GetRequiredUmbracoContext(this IUmbracoContextAccessor umbracoContextAccessor) + { + if (umbracoContextAccessor == null) throw new ArgumentNullException(nameof(umbracoContextAccessor)); + + var umbracoContext = umbracoContextAccessor.UmbracoContext; + + if(umbracoContext is null) throw new InvalidOperationException("UmbracoContext is null"); + + return umbracoContext; + } + } +} diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs index 334183a48c..d4e22bbfde 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; +using Umbraco.Net; namespace Umbraco.Core.Compose { @@ -12,18 +13,18 @@ namespace Umbraco.Core.Compose private readonly IRuntimeState _runtimeState; private readonly ILogger _logger; private readonly IIOHelper _ioHelper; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for // package.manifest chances and restarts the application on any change private ManifestWatcher _mw; - public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment) + public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger, IIOHelper ioHelper, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _runtimeState = runtimeState; _logger = logger; _ioHelper = ioHelper; - _hostingEnvironment = hostingEnvironment; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } public void Initialize() @@ -36,7 +37,7 @@ namespace Umbraco.Core.Compose var appPlugins = _ioHelper.MapPath("~/App_Plugins/"); if (Directory.Exists(appPlugins) == false) return; - _mw = new ManifestWatcher(_logger, _hostingEnvironment); + _mw = new ManifestWatcher(_logger, _umbracoApplicationLifetime); _mw.Start(Directory.GetDirectories(appPlugins)); } diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs similarity index 80% rename from src/Umbraco.Web/Install/InstallHelper.cs rename to src/Umbraco.Infrastructure/Intall/InstallHelper.cs index f7344d0b0b..a5b4f71d8e 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallHelper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Cookie; @@ -11,9 +10,9 @@ using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Serialization; using Umbraco.Core.Services; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install @@ -22,25 +21,28 @@ namespace Umbraco.Web.Install { private static HttpClient _httpClient; private readonly DatabaseBuilder _databaseBuilder; - private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; private readonly IUmbracoVersion _umbracoVersion; private readonly IConnectionStrings _connectionStrings; private readonly IInstallationService _installationService; private readonly ICookieManager _cookieManager; + private readonly IUserAgentProvider _userAgentProvider; + private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory; + private readonly IJsonSerializer _jsonSerializer; private InstallationType? _installationType; - public InstallHelper(IHttpContextAccessor httpContextAccessor, - DatabaseBuilder databaseBuilder, + public InstallHelper(DatabaseBuilder databaseBuilder, ILogger logger, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IConnectionStrings connectionStrings, IInstallationService installationService, - ICookieManager cookieManager) + ICookieManager cookieManager, + IUserAgentProvider userAgentProvider, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IJsonSerializer jsonSerializer) { - _httpContextAccessor = httpContextAccessor; _logger = logger; _globalSettings = globalSettings; _umbracoVersion = umbracoVersion; @@ -48,6 +50,9 @@ namespace Umbraco.Web.Install _connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings)); _installationService = installationService; _cookieManager = cookieManager; + _userAgentProvider = userAgentProvider; + _umbracoDatabaseFactory = umbracoDatabaseFactory; + _jsonSerializer = jsonSerializer; } public InstallationType GetInstallationType() @@ -57,11 +62,9 @@ namespace Umbraco.Web.Install public async Task InstallStatus(bool isCompleted, string errorMsg) { - - var httpContext = _httpContextAccessor.GetRequiredHttpContext(); try { - var userAgent = httpContext.Request.UserAgent; + var userAgent = _userAgentProvider.GetUserAgent(); // Check for current install Id var installId = Guid.NewGuid(); @@ -88,7 +91,7 @@ namespace Umbraco.Web.Install { // we don't have DatabaseProvider anymore... doing it differently //dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString(); - dbProvider = GetDbProviderString(Current.SqlContext); + dbProvider = _umbracoDatabaseFactory.SqlContext.SqlSyntax.DbProvider; } var installLog = new InstallLog(installId: installId, isUpgrade: IsBrandNewInstall == false, @@ -105,23 +108,6 @@ namespace Umbraco.Web.Install } } - internal static string GetDbProviderString(ISqlContext sqlContext) - { - var dbProvider = string.Empty; - - // we don't have DatabaseProvider anymore... - //dbProvider = ApplicationContext.Current.DatabaseContext.DatabaseProvider.ToString(); - // - // doing it differently - var syntax = sqlContext.SqlSyntax; - if (syntax is SqlCeSyntaxProvider) - dbProvider = "SqlServerCE"; - else if (syntax is SqlServerSyntaxProvider) - dbProvider = (syntax as SqlServerSyntaxProvider).ServerVersion.IsAzure ? "SqlAzure" : "SqlServer"; - - return dbProvider; - } - /// /// Checks if this is a brand new install meaning that there is no configured version and there is no configured database connection /// @@ -162,7 +148,10 @@ namespace Umbraco.Web.Install using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) { var response = _httpClient.SendAsync(request).Result; - packages = response.Content.ReadAsAsync>().Result.ToList(); + + + var json = response.Content.ReadAsStringAsync().Result; + packages = _jsonSerializer.Deserialize>(json).ToList(); } } catch (AggregateException ex) diff --git a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs similarity index 85% rename from src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs index 164c142e0c..fb8201600c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/ConfigureMachineKey.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading.Tasks; -using System.Web.Configuration; using System.Xml.Linq; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Security; using Umbraco.Web.Install.Models; @@ -12,13 +12,15 @@ namespace Umbraco.Web.Install.InstallSteps "ConfigureMachineKey", "machinekey", 2, "Updating some security settings...", PerformsAppRestart = true)] - internal class ConfigureMachineKey : InstallSetupStep + public class ConfigureMachineKey : InstallSetupStep { private readonly IIOHelper _ioHelper; + private readonly IMachineKeyConfig _machineKeyConfig; - public ConfigureMachineKey(IIOHelper ioHelper) + public ConfigureMachineKey(IIOHelper ioHelper, IMachineKeyConfig machineKeyConfig) { _ioHelper = ioHelper; + _machineKeyConfig = machineKeyConfig; } public override string View => HasMachineKey() == false ? base.View : ""; @@ -27,10 +29,9 @@ namespace Umbraco.Web.Install.InstallSteps /// Don't display the view or execute if a machine key already exists /// /// - private static bool HasMachineKey() + private bool HasMachineKey() { - var section = (MachineKeySection) WebConfigurationManager.GetSection("system.web/machineKey"); - return section.ElementInformation.Source != null; + return _machineKeyConfig.HasMachineKey; } /// diff --git a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs similarity index 73% rename from src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs index dcf115082f..ab648a261e 100644 --- a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs @@ -1,44 +1,31 @@ using System.Threading.Tasks; -using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Services; -using Umbraco.Web.Cache; -using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web.Security; - namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, "UmbracoVersion", 50, "Installation is complete!, get ready to be redirected to your new CMS.", PerformsAppRestart = true)] - internal class SetUmbracoVersionStep : InstallSetupStep + public class SetUmbracoVersionStep : InstallSetupStep { - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly InstallHelper _installHelper; private readonly IGlobalSettings _globalSettings; - private readonly IUserService _userService; private readonly IUmbracoVersion _umbracoVersion; - private readonly IIOHelper _ioHelper; - public SetUmbracoVersionStep(IHttpContextAccessor httpContextAccessor, InstallHelper installHelper, IGlobalSettings globalSettings, IUserService userService, IUmbracoVersion umbracoVersion, IIOHelper ioHelper) + public SetUmbracoVersionStep(IUmbracoContextAccessor umbracoContextAccessor, InstallHelper installHelper, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion) { - _httpContextAccessor = httpContextAccessor; + _umbracoContextAccessor = umbracoContextAccessor; _installHelper = installHelper; _globalSettings = globalSettings; - _userService = userService; _umbracoVersion = umbracoVersion; - _ioHelper = ioHelper; } public override Task ExecuteAsync(object model) { - var security = new WebSecurity(_httpContextAccessor, _userService, _globalSettings, _ioHelper); - + var security = _umbracoContextAccessor.GetRequiredUmbracoContext().Security; if (security.IsAuthenticated() == false && _globalSettings.ConfigurationStatus.IsNullOrWhiteSpace()) { security.PerformLogin(-1); diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs similarity index 93% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs index 624f5897c7..d8986cacb7 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitDownloadStep.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Web; using Umbraco.Core.Services; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Packaging; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -20,14 +18,16 @@ namespace Umbraco.Web.Install.InstallSteps private readonly InstallHelper _installHelper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IUmbracoVersion _umbracoVersion; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IContentService _contentService; private readonly IPackagingService _packageService; - public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoVersion umbracoVersion) + public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoVersion umbracoVersion, IUmbracoApplicationLifetime umbracoApplicationLifetime) { _installHelper = installHelper; _umbracoContextAccessor = umbracoContextAccessor; _umbracoVersion = umbracoVersion; + _umbracoApplicationLifetime = umbracoApplicationLifetime; _contentService = contentService; _packageService = packageService; } @@ -54,7 +54,7 @@ namespace Umbraco.Web.Install.InstallSteps var (packageFile, packageId) = await DownloadPackageFilesAsync(starterKitId.Value); - UmbracoApplication.Restart(); + _umbracoApplicationLifetime.Restart(); return new InstallSetupResult(new Dictionary { diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs similarity index 83% rename from src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs rename to src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs index c9688fed4c..cc269408f4 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/StarterKitInstallStep.cs @@ -2,9 +2,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using System.Web; using Umbraco.Core.Services; -using Umbraco.Web.Composing; +using Umbraco.Net; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -14,13 +13,13 @@ namespace Umbraco.Web.Install.InstallSteps PerformsAppRestart = true)] internal class StarterKitInstallStep : InstallSetupStep { - private readonly IHttpContextAccessor _httContextAccessor; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IPackagingService _packagingService; - public StarterKitInstallStep(IHttpContextAccessor httContextAccessor, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) + public StarterKitInstallStep(IUmbracoApplicationLifetime umbracoApplicationLifetime, IUmbracoContextAccessor umbracoContextAccessor, IPackagingService packagingService) { - _httContextAccessor = httContextAccessor; + _umbracoApplicationLifetime = umbracoApplicationLifetime; _umbracoContextAccessor = umbracoContextAccessor; _packagingService = packagingService; } @@ -34,7 +33,9 @@ namespace Umbraco.Web.Install.InstallSteps InstallBusinessLogic(packageId); - UmbracoApplication.Restart(_httContextAccessor.GetRequiredHttpContext()); + _umbracoApplicationLifetime.Restart(); + + return Task.FromResult(null); } diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 7ae001bf24..58f9a4b68a 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -77,12 +77,13 @@ namespace Umbraco.Core.Persistence.SqlSyntax string ConvertIntegerToOrderableString { get; } string ConvertDateToOrderableString { get; } string ConvertDecimalToOrderableString { get; } - + /// /// Returns the default isolation level for the database /// IsolationLevel DefaultIsolationLevel { get; } + string DbProvider { get; } IEnumerable GetTablesInSchema(IDatabase db); IEnumerable GetColumnsInSchema(IDatabase db); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index a478c88412..8d3a41e5bf 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -175,6 +175,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(x => new Tuple(x.TableName, x.ColumnName, x.Name, x.Definition)); } + public override string DbProvider => ServerVersion.IsAzure ? "SqlAzure" : "SqlServer"; + public override IEnumerable GetTablesInSchema(IDatabase db) { var items = db.Fetch("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = (SELECT SCHEMA_NAME())"); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index b2e03df96e..3df97c0b4f 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -202,6 +202,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } public abstract IsolationLevel DefaultIsolationLevel { get; } + public abstract string DbProvider { get; } public virtual IEnumerable GetTablesInSchema(IDatabase db) { diff --git a/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs b/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs index 2ed0fb878c..7abcc60c86 100644 --- a/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Persistance.SqlCe/SqlCeSyntaxProvider.cs @@ -55,6 +55,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.RepeatableRead; + public override string DbProvider => "SqlServerCE"; public override string FormatColumnRename(string tableName, string oldName, string newName) { diff --git a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs index f9b1f1019b..75c4bcb660 100644 --- a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs +++ b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs @@ -49,11 +49,6 @@ namespace Umbraco.Web.Hosting } public string ToAbsolute(string virtualPath, string root) => VirtualPathUtility.ToAbsolute(virtualPath, root); - public void LazyRestartApplication() - { - HttpRuntime.UnloadAppDomain(); - } - public void RegisterObject(IRegisteredObject registeredObject) { var wrapped = new RegisteredObjectWrapper(registeredObject); diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs new file mode 100644 index 0000000000..245e8ea374 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Web; +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public bool IsRestarting { get; set; } + + public void Restart() + { + IsRestarting = true; + + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext != null) + { + // unload app domain - we must null out all identities otherwise we get serialization errors + // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue + httpContext.User = null; + } + + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } + } +} diff --git a/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs b/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs new file mode 100644 index 0000000000..bd37b62531 --- /dev/null +++ b/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs @@ -0,0 +1,19 @@ +using Umbraco.Net; + +namespace Umbraco.Web.AspNet +{ + public class AspNetUserAgentProvider : IUserAgentProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public AspNetUserAgentProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + public string GetUserAgent() + { + return _httpContextAccessor.GetRequiredHttpContext().Request.UserAgent; + } + } +} diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 081b808cac..47c36177fe 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -263,7 +263,8 @@ namespace Umbraco.Web.Composing public static IUmbracoVersion UmbracoVersion => Factory.GetInstance(); public static IPublishedUrlProvider PublishedUrlProvider => Factory.GetInstance(); public static IMenuItemCollectionFactory MenuItemCollectionFactory => Factory.GetInstance(); - public static MembershipHelper MembershipHelper => Factory.GetInstance(); + public static MembershipHelper MembershipHelper => Factory.GetInstance(); + public static IUmbracoApplicationLifetime UmbracoApplicationLifetime => Factory.GetInstance(); #endregion } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 4563547346..a0ab2de68a 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Net; using Umbraco.Web.JavaScript; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; @@ -39,6 +40,7 @@ namespace Umbraco.Web.Editors private readonly IUmbracoVersion _umbracoVersion; private readonly IIOHelper _ioHelper; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; public PackageInstallController( IGlobalSettings globalSettings, @@ -53,11 +55,13 @@ namespace Umbraco.Web.Editors IUmbracoVersion umbracoVersion, UmbracoMapper umbracoMapper, IIOHelper ioHelper, - IPublishedUrlProvider publishedUrlProvider) + IPublishedUrlProvider publishedUrlProvider, + IUmbracoApplicationLifetime umbracoApplicationLifetime) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper, umbracoMapper, publishedUrlProvider) { _umbracoVersion = umbracoVersion; _ioHelper = ioHelper; + _umbracoApplicationLifetime = umbracoApplicationLifetime; } /// @@ -325,7 +329,7 @@ namespace Umbraco.Web.Editors var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool - UmbracoApplication.Restart(Request.TryGetHttpContext().Result); + _umbracoApplicationLifetime.Restart(); model.IsRestarting = true; @@ -337,12 +341,8 @@ namespace Umbraco.Web.Editors { if (model.IsRestarting == false) return model; - //check for the key, if it's not there we're are restarted - if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting") == false) - { - //reset it - model.IsRestarting = false; - } + model.IsRestarting = _umbracoApplicationLifetime.IsRestarting; + return model; } diff --git a/src/Umbraco.Web/HttpContextAccessorExtensions.cs b/src/Umbraco.Web/HttpContextAccessorExtensions.cs index 2076510ce0..8901cf692c 100644 --- a/src/Umbraco.Web/HttpContextAccessorExtensions.cs +++ b/src/Umbraco.Web/HttpContextAccessorExtensions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Web { public static HttpContextBase GetRequiredHttpContext(this IHttpContextAccessor httpContextAccessor) { + if (httpContextAccessor == null) throw new ArgumentNullException(nameof(httpContextAccessor)); var httpContext = httpContextAccessor.HttpContext; if(httpContext is null) throw new InvalidOperationException("HttpContext is null"); diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 85861a1496..9990644db8 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -331,7 +331,7 @@ namespace Umbraco.Web.Macros /// The text output of the macro execution. private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) { - var engine = new PartialViewMacroEngine(); + var engine = new PartialViewMacroEngine(_umbracoContextAccessor, _httpContextAccessor, _ioHelper); return engine.Execute(macro, content); } diff --git a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs index ecfda80399..0099ff220a 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs @@ -5,6 +5,7 @@ using System.Web.Routing; using System.Web.WebPages; using Umbraco.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Composing; @@ -15,37 +16,24 @@ namespace Umbraco.Web.Macros /// public class PartialViewMacroEngine { - private readonly Func _getHttpContext; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IIOHelper _ioHelper; private readonly Func _getUmbracoContext; - public PartialViewMacroEngine() + public PartialViewMacroEngine(IUmbracoContextAccessor umbracoContextAccessor, IHttpContextAccessor httpContextAccessor, IIOHelper ioHelper) { - _getHttpContext = () => - { - if (HttpContext.Current == null) - throw new InvalidOperationException($"The {GetType()} cannot execute with a null HttpContext.Current reference."); - return new HttpContextWrapper(HttpContext.Current); - }; + _httpContextAccessor = httpContextAccessor; + _ioHelper = ioHelper; _getUmbracoContext = () => { - if (Current.UmbracoContext == null) + var context = umbracoContextAccessor.UmbracoContext; + if (context == null) throw new InvalidOperationException($"The {GetType()} cannot execute with a null UmbracoContext.Current reference."); - return Current.UmbracoContext; + return context; }; } - /// - /// Constructor generally used for unit testing - /// - /// - /// - internal PartialViewMacroEngine(HttpContextBase httpContext, IUmbracoContext umbracoContext) - { - _getHttpContext = () => httpContext; - _getUmbracoContext = () => umbracoContext; - } - public bool Validate(string code, string tempFileName, IPublishedContent currentPage, out string errorMessage) { var temp = GetVirtualPathFromPhysicalPath(tempFileName); @@ -68,7 +56,7 @@ namespace Umbraco.Web.Macros if (content == null) throw new ArgumentNullException(nameof(content)); if (macro.MacroSource.IsNullOrWhiteSpace()) throw new ArgumentException("The MacroSource property of the macro object cannot be null or empty"); - var http = _getHttpContext(); + var httpContext = _httpContextAccessor.GetRequiredHttpContext(); var umbCtx = _getUmbracoContext(); var routeVals = new RouteData(); routeVals.Values.Add("controller", "PartialViewMacro"); @@ -79,13 +67,14 @@ namespace Umbraco.Web.Macros var viewContext = new ViewContext { ViewData = new ViewDataDictionary() }; //try and extract the current view context from the route values, this would be set in the UmbracoViewPage or in // the UmbracoPageResult if POSTing to an MVC controller but rendering in Webforms - if (http.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext)) + if (httpContext.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext)) { - viewContext = (ViewContext)http.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext]; + viewContext = (ViewContext)httpContext.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext]; } routeVals.DataTokens.Add("ParentActionViewContext", viewContext); - var request = new RequestContext(http, routeVals); + var request = new RequestContext(httpContext, routeVals); + string output; using (var controller = new PartialViewMacroController(macro, content)) { @@ -103,7 +92,7 @@ namespace Umbraco.Web.Macros private string GetVirtualPathFromPhysicalPath(string physicalPath) { - var rootpath = _getHttpContext().Server.MapPath("~/"); + var rootpath = _ioHelper.MapPath("~/"); physicalPath = physicalPath.Replace(rootpath, ""); physicalPath = physicalPath.Replace("\\", "/"); return "~/" + physicalPath; diff --git a/src/Umbraco.Web/Mvc/ContentModelBinder.cs b/src/Umbraco.Web/Mvc/ContentModelBinder.cs index 052938807a..a66f8e9089 100644 --- a/src/Umbraco.Web/Mvc/ContentModelBinder.cs +++ b/src/Umbraco.Web/Mvc/ContentModelBinder.cs @@ -151,9 +151,15 @@ namespace Umbraco.Web.Mvc var context = HttpContext.Current; if (context == null) + { AppDomain.Unload(AppDomain.CurrentDomain); + } else - UmbracoApplication.Restart(new HttpContextWrapper(context)); + { + Current.UmbracoApplicationLifetime.Restart(); + } + + } throw new ModelBindingException(msg.ToString()); diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 35aae084de..e4b50a248c 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -49,6 +49,7 @@ using Current = Umbraco.Web.Composing.Current; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; using Umbraco.Core.Models; +using Umbraco.Web.AspNet; using Umbraco.Web.Models; namespace Umbraco.Web.Runtime @@ -64,9 +65,11 @@ namespace Umbraco.Web.Runtime composition.Register(); composition.Register(); + composition.Register(); composition.Register(); composition.Register(); composition.Register(); + composition.Register(Lifetime.Singleton); composition.Register(); composition.Register(Lifetime.Singleton); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 27f372753a..fb96847eb4 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -134,7 +134,9 @@ + + @@ -158,8 +160,6 @@ - - @@ -270,7 +270,6 @@ - @@ -359,7 +358,6 @@ - @@ -455,7 +453,6 @@ - diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 90d575bf73..098fc94724 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -36,46 +36,5 @@ namespace Umbraco.Web return new WebRuntime(this, configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom); } - - /// - /// Restarts the Umbraco application. - /// - public static void Restart() - { - // see notes in overload - - var httpContext = HttpContext.Current; - if (httpContext != null) - { - httpContext.Application.Add("AppPoolRestarting", true); - httpContext.User = null; - } - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } - - /// - /// Restarts the Umbraco application. - /// - public static void Restart(HttpContextBase httpContext) - { - if (httpContext != null) - { - // we're going to put an application wide flag to show that the application is about to restart. - // we're doing this because if there is a script checking if the app pool is fully restarted, then - // it can check if this flag exists... if it does it means the app pool isn't restarted yet. - httpContext.Application.Add("AppPoolRestarting", true); - - // unload app domain - we must null out all identities otherwise we get serialization errors - // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue - httpContext.User = null; - } - - if (HttpContext.Current != null) - HttpContext.Current.User = null; - - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } } } diff --git a/src/Umbraco.Web/UmbracoContextFactory.cs b/src/Umbraco.Web/UmbracoContextFactory.cs index 466bf68576..4090da5a6d 100644 --- a/src/Umbraco.Web/UmbracoContextFactory.cs +++ b/src/Umbraco.Web/UmbracoContextFactory.cs @@ -70,7 +70,6 @@ namespace Umbraco.Web _variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture); } - var webSecurity = new WebSecurity(_httpContextAccessor, _userService, _globalSettings, _ioHelper); return new UmbracoContext(_httpContextAccessor, _publishedSnapshotService, webSecurity, _globalSettings, _variationContextAccessor, _ioHelper, _uriUtility, _cookieManager); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index e1e74ead5e..679fbb789c 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml.XPath; +using Umbraco.Composing; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; -using Umbraco.Web.Composing; using Umbraco.Web.Mvc; namespace Umbraco.Web