diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index e81802b50c..ecbc5648c2 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -24,34 +24,36 @@ namespace Umbraco.Web.Install steps.AddRange(new InstallSetupStep[] { new FilePermissionsStep() - { - Name = "Permissions", + { ServerOrder = 0, }, new UserStep(umbracoContext.Application) { - Name = "User", - ServerOrder = 2, + ServerOrder = 4, }, new DatabaseConfigureStep(umbracoContext.Application) { - Name = "Database", ServerOrder = 1, }, + new DatabaseInstallStep(umbracoContext.Application) + { + ServerOrder = 2, + }, + new DatabaseUpgradeStep(umbracoContext.Application) + { + ServerOrder = 3, + }, new StarterKitDownloadStep() { - Name = "StarterKitDownload", - ServerOrder = 3, + ServerOrder = 5, }, new StarterKitInstallStep(umbracoContext.Application, umbracoContext.HttpContext) { - Name = "StarterKitInstall", - ServerOrder = 4, + ServerOrder = 6, }, new StarterKitCleanupStep() { - Name = "StarterKitCleanup", - ServerOrder = 5, + ServerOrder = 7, } }); return steps; diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs index 2344bf1f95..b5bbbe3a91 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs @@ -1,35 +1,19 @@ using System; using System.Collections.Generic; +using System.Configuration; +using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Persistence; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { - //internal class DatabaseInstallStep : IInstallStep - //{ - // private readonly ApplicationContext _applicationContext; - - // public DatabaseInstallStep(ApplicationContext applicationContext) - // { - // _applicationContext = applicationContext; - // } - - // public IDictionary Setup(object model) - // { - // if (CheckConnection(database) == false) - // { - // throw new InvalidOperationException("Could not connect to the database"); - // } - // ConfigureConnection(database); - // return null; - // } - //} - + [InstallSetupStep("DatabaseConfigure", "database")] internal class DatabaseConfigureStep : InstallSetupStep { private readonly ApplicationContext _applicationContext; @@ -106,7 +90,48 @@ namespace Umbraco.Web.Install.InstallSteps public override string View { - get { return "database"; } + get { return ShouldDisplayView() ? base.View : ""; } + } + + private bool ShouldDisplayView() + { + //If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading. + var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + + var dbIsSqlCe = false; + if (databaseSettings != null && databaseSettings.ProviderName != null) + dbIsSqlCe = databaseSettings.ProviderName == "System.Data.SqlServerCe.4.0"; + var sqlCeDatabaseExists = false; + if (dbIsSqlCe) + { + var datasource = databaseSettings.ConnectionString.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()); + var filePath = datasource.Replace("Data Source=", string.Empty); + sqlCeDatabaseExists = File.Exists(filePath); + } + + // Either the connection details are not fully specified or it's a SQL CE database that doesn't exist yet + if (databaseSettings == null + || string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) || string.IsNullOrWhiteSpace(databaseSettings.ProviderName) + || (dbIsSqlCe && sqlCeDatabaseExists == false)) + { + return true; + } + else + { + //Since a connection string was present we verify whether this is an upgrade or an empty db + var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema(); + var determinedVersion = result.DetermineInstalledVersion(); + if (determinedVersion.Equals(new Version(0, 0, 0))) + { + //Fresh install + return false; + } + else + { + //Upgrade + return false; + } + } } } } diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs new file mode 100644 index 0000000000..a901bba842 --- /dev/null +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Web.Install.Models; + +namespace Umbraco.Web.Install.InstallSteps +{ + [InstallSetupStep("DatabaseInstall", "")] + internal class DatabaseInstallStep : InstallSetupStep + { + private readonly ApplicationContext _applicationContext; + + public DatabaseInstallStep(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + public override IDictionary Execute(object model) + { + var result = _applicationContext.DatabaseContext.CreateDatabaseSchemaAndData(); + if (result.RequiresUpgrade == false) + { + HandleConnectionStrings(); + return new Dictionary + { + {"upgrade", true} + }; + } + return null; + } + + internal static void HandleConnectionStrings() + { + // Remove legacy umbracoDbDsn configuration setting if it exists and connectionstring also exists + if (ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName] != null) + { + GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName); + } + else + { + var ex = new ArgumentNullException(string.Format("ConfigurationManager.ConnectionStrings[{0}]", GlobalSettings.UmbracoConnectionName), "Install / upgrade did not complete successfully, umbracoDbDSN was not set in the connectionStrings section"); + LogHelper.Error("", ex); + throw ex; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs new file mode 100644 index 0000000000..83d3279a11 --- /dev/null +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Web.Install.Models; + +namespace Umbraco.Web.Install.InstallSteps +{ + [InstallSetupStep("DatabaseUpgrade", "")] + internal class DatabaseUpgradeStep : InstallSetupStep + { + private readonly ApplicationContext _applicationContext; + + public DatabaseUpgradeStep(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + public override IDictionary Execute(object model) + { + var installSteps = InstallStatusTracker.GetStatus(); + //this step relies on the preious one completed - because it has stored some information we need + if (installSteps.Any(x => x.Key == "DatabaseConfigure") == false) + { + throw new InvalidOperationException("Could not find previous step: DatabaseConfigure of the installation, package install cannot continue"); + } + var previousStep = installSteps["DatabaseConfigure"]; + var upgrade = previousStep.AdditionalData.ContainsKey("upgrade"); + + if (upgrade) + { + LogHelper.Info("Running 'Upgrade' service"); + + var result = _applicationContext.DatabaseContext.UpgradeSchemaAndData(); + + DatabaseInstallStep.HandleConnectionStrings(); + } + + return null; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs index 0b2a03359a..7babeaa2f8 100644 --- a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs @@ -8,6 +8,7 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { + [InstallSetupStep("Permissions", "")] internal class FilePermissionsStep : InstallSetupStep { public override IDictionary Execute(object model) @@ -123,9 +124,5 @@ namespace Umbraco.Web.Install.InstallSteps } } - public override string View - { - get { return string.Empty; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/IInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/IInstallStep.cs deleted file mode 100644 index dd191ea31d..0000000000 --- a/src/Umbraco.Web/Install/InstallSteps/IInstallStep.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Web.Install.InstallSteps -{ - internal interface IInstallStep - { - IDictionary Setup(T model); - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs index b4292d7339..c220dbc702 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs @@ -7,6 +7,7 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { + [InstallSetupStep("StarterKitCleanup", "")] internal class StarterKitCleanupStep : InstallSetupStep { public override IDictionary Execute(object model) @@ -36,9 +37,5 @@ namespace Umbraco.Web.Install.InstallSteps library.RefreshContent(); } - public override string View - { - get { return string.Empty; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 12ed251baa..0db3a66905 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -6,6 +6,7 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { + [InstallSetupStep("StarterKitDownload", "starterKit")] internal class StarterKitDownloadStep : InstallSetupStep { private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; @@ -52,9 +53,5 @@ namespace Umbraco.Web.Install.InstallSteps } - public override string View - { - get { return "starterKit"; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 4015214620..56aaae63cb 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -7,6 +7,7 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { + [InstallSetupStep("StarterKitInstall", "")] internal class StarterKitInstallStep : InstallSetupStep { private readonly ApplicationContext _applicationContext; @@ -46,9 +47,5 @@ namespace Umbraco.Web.Install.InstallSteps installer.InstallBusinessLogic(manifestId, packageFile); } - public override string View - { - get { return string.Empty; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/UserStep.cs b/src/Umbraco.Web/Install/InstallSteps/UserStep.cs index 498b56d7d4..7cea722342 100644 --- a/src/Umbraco.Web/Install/InstallSteps/UserStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/UserStep.cs @@ -7,6 +7,8 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { + + [InstallSetupStep("User", "user")] internal class UserStep : InstallSetupStep { private readonly ApplicationContext _applicationContext; @@ -65,9 +67,5 @@ namespace Umbraco.Web.Install.InstallSteps return null; } - public override string View - { - get { return "user"; } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs index 4308310ecc..5349ccbc50 100644 --- a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs +++ b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs @@ -11,6 +11,18 @@ namespace Umbraco.Web.Install.Models [DataContract(Name = "step", Namespace = "")] public abstract class InstallSetupStep : InstallSetupStep { + protected InstallSetupStep() + { + var att = GetType().GetCustomAttribute(false); + if (att == null) + { + throw new InvalidOperationException("Each step must be attributed"); + } + _attribute = att; + } + + private readonly InstallSetupStepAttribute _attribute; + /// /// Defines the step model type on the server side so we can bind it /// @@ -27,18 +39,32 @@ namespace Umbraco.Web.Install.Models { get { return View.IsNullOrWhiteSpace() == false; } } - //[IgnoreDataMember] - //public Func> ExecuteCallback { get; set; } + + public override string View + { + get { return _attribute.View; } + } } [DataContract(Name = "step", Namespace = "")] public abstract class InstallSetupStep { + protected InstallSetupStep() + { + var att = GetType().GetCustomAttribute(false); + if (att == null) + { + throw new InvalidOperationException("Each step must be attributed"); + } + Name = att.Name; + View = att.View; + } + [DataMember(Name = "name")] - public string Name { get; set; } + public string Name { get; private set; } [DataMember(Name = "view")] - public abstract string View { get; } + public virtual string View { get; private set; } /// /// Defines what order this step needs to execute on the server side since the diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStepAttribute.cs b/src/Umbraco.Web/Install/Models/InstallSetupStepAttribute.cs new file mode 100644 index 0000000000..e14eece2b3 --- /dev/null +++ b/src/Umbraco.Web/Install/Models/InstallSetupStepAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Umbraco.Web.Install.Models +{ + public sealed class InstallSetupStepAttribute : Attribute + { + public InstallSetupStepAttribute(string name, string view) + { + Name = name; + View = view; + } + + public string Name { get; private set; } + public string View { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 14ab1497e4..7b6cd2253b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -305,8 +305,9 @@ + + - @@ -315,6 +316,7 @@ +