Updates the RequiresExecution logic so we can skip steps based on some user input (i.e. skip the starter kit installation). Fixes the start kit js stuff to update the model correctly and continue. Fixes the error view to restart - it cannot just go back because there might not be a view there.

This commit is contained in:
Shannon
2014-03-05 14:30:17 +11:00
parent 893f86d34c
commit a40ba2f27e
24 changed files with 180 additions and 68 deletions

View File

@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Install
var expected = new[]
{
typeof (FilePermissionsStep), typeof (UserStep), typeof(MajorVersion7UpgradeReport), typeof (DatabaseConfigureStep), typeof (DatabaseInstallStep),
typeof (FilePermissionsStep), typeof (NewInstallStep), typeof(MajorVersion7UpgradeReport), typeof (DatabaseConfigureStep), typeof (DatabaseInstallStep),
typeof (DatabaseUpgradeStep), typeof (StarterKitDownloadStep), typeof (StarterKitInstallStep), typeof (StarterKitCleanupStep),
typeof (SetUmbracoVersionStep)
};

View File

@@ -20,4 +20,8 @@ angular.module("umbraco.install").controller("Umbraco.InstallerController",
$scope.gotoStep = function(step){
installerService.gotoNamedStep(step);
};
$scope.restart = function () {
installerService.gotoStep(0);
};
});

View File

@@ -89,20 +89,20 @@ angular.module("umbraco.install").factory('installerService', function($q, $time
},
gotoNamedStep : function(stepName){
var step = _.find(service.status.steps, function(s, index){
if (s.view && s.name === stepName) {
service.status.index = index;
return true;
}
return false;
});
step.view = resolveView(step.view);
if(!step.model){
step.model = {};
var step = _.find(service.status.steps, function(s, index){
if (s.view && s.name === stepName) {
service.status.index = index;
return true;
}
service.retrieveCurrentStep();
service.status.current = step;
return false;
});
step.view = resolveView(step.view);
if(!step.model){
step.model = {};
}
service.retrieveCurrentStep();
service.status.current = step;
},
/**

View File

@@ -0,0 +1,13 @@
<div>
<h1>Continue Umbraco Installation</h1>
<p>
You see this screen because your Umbraco installation did not complete correctly.
</p>
<p>
Simply click <strong>continue</strong> below to be guided through the rest of the installation process.
</p>
<p>
<button class="btn btn-success" ng-click="install()">Continue</button>
</p>
</div>

View File

@@ -1,7 +1,9 @@
<div class="error">
<h1>Error during installation</h1>
<h1>Error during installation</h1>
<p class="message">{{installer.current.model.message}}</p>
<p class="message">{{installer.current.model.message}}</p>
<button class="btn btn-success" ng-click="gotoStep(installer.current.model.step)">Go back</button>
<p><small>See log for full details</small></p>
<button class="btn btn-success" ng-click="restart()">Go back</button>
</div>

View File

@@ -4,7 +4,8 @@ angular.module("umbraco.install").controller("Umbraco.Installer.PackagesControll
$scope.packages = response.data;
});
$scope.setPackageAndContinue = function (pck) {
$scope.setPackageAndContinue = function (pckId) {
installerService.status.current.model = pckId;
installerService.forward();
};

View File

@@ -6,14 +6,15 @@
and simple foundation to build on top of.
</p>
<ul class="thumbnails">
<li class="span3" ng-repeat="package in packages">
<a href ng-click="installer.current.model = package.id; install()" class="thumbnail">
<img ng-src="http://our.umbraco.org{{package.thumbnail}}" alt="{{package.name}}">
</a>
</li>
</ul>
<ul class="thumbnails">
<li class="span3" ng-repeat="pck in packages">
<a href ng-click="setPackageAndContinue(pck.id)" class="thumbnail">
<img ng-src="http://our.umbraco.org{{pck.thumbnail}}" alt="{{pck.name}}">
</a>
</li>
</ul>
<a href ng-click="install()" class="btn btn-link">No thanks, I do not want to install a starter website</a>
<a href ng-click="setPackageAndContinue('00000000-0000-0000-0000-000000000000')" class="btn btn-link">
No thanks, I do not want to install a starter website
</a>
</div>

View File

@@ -5,7 +5,7 @@
it is completely harmless and will simply ensure your website is kept as fast, secure and uptodate as possible.
</p>
<p>
Simply click <strong>continue</strong> below to be guided through the rest of the upgrade</p>
Simply click <strong>continue</strong> below to be guided through the rest of the upgrade
</p>
<p>

View File

@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="1678677019" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<clientDependency version="394389720" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<!--
This section is used for Web Forms only, the enableCompositeFiles="true" is optional and by default is set to true.

View File

@@ -135,9 +135,9 @@ namespace Umbraco.Web.Install.Controllers
{
instruction = installModel.Instructions[step.Name];
}
//If this step doesn't require execution then continue to the next one, this is just a fail-safe check.
if (step.RequiresExecution() == false)
if (StepRequiresExecution(step, instruction) == false)
{
//set this as complete and continue
InstallStatusTracker.SetComplete(installModel.InstallId, stepStatus.Name, null);
@@ -152,7 +152,7 @@ namespace Umbraco.Web.Install.Controllers
InstallStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData != null ? setupData.SavedStepData : null);
//Determine's the next step in the queue and dequeue's any items that don't need to execute
var nextStep = IterateNextRequiredStep(step, queue, installModel.InstallId);
var nextStep = IterateNextRequiredStep(step, queue, installModel.InstallId, installModel);
//check if there's a custom view to return for this step
if (setupData != null && setupData.View.IsNullOrWhiteSpace() == false)
@@ -164,6 +164,9 @@ namespace Umbraco.Web.Install.Controllers
}
catch (Exception ex)
{
LogHelper.Error<InstallApiController>("An error occurred during installation step " + step.Name, ex);
if (ex is TargetInvocationException && ex.InnerException != null)
{
ex = ex.InnerException;
@@ -200,23 +203,36 @@ namespace Umbraco.Web.Install.Controllers
/// <param name="current"></param>
/// <param name="queue"></param>
/// <param name="installId"></param>
/// <param name="installModel"></param>
/// <returns></returns>
private string IterateNextRequiredStep(InstallSetupStep current, Queue<InstallTrackingItem> queue, Guid installId)
private string IterateNextRequiredStep(InstallSetupStep current, Queue<InstallTrackingItem> queue, Guid installId, InstallInstructions installModel)
{
if (queue.Count > 0)
{
var next = queue.Peek();
var step = InstallHelper.GetAllSteps().Single(x => x.Name == next.Name);
//If the current step restarts the app pool then we must simply return the next one in the queue,
// we cannot peek ahead as the next step might rely on the app restart and therefore RequiresExecution
// will rely on that too.
//Otherwise if it requires execution then of course return it.
if (current.PerformsAppRestart || step.RequiresExecution())
// will rely on that too.
if (current.PerformsAppRestart)
{
return step.Name;
}
JToken instruction = null;
//If this step has any instructions then extract them
if (installModel.Instructions.Any(x => x.Key == step.Name))
{
instruction = installModel.Instructions[step.Name];
}
//if the step requires execution then return it's name
if (StepRequiresExecution(step, instruction))
{
return step.Name;
}
//this step no longer requires execution, this could be due to a new config change during installation,
// so we'll dequeue this one from the queue and recurse
queue.Dequeue();
@@ -225,13 +241,37 @@ namespace Umbraco.Web.Install.Controllers
InstallStatusTracker.SetComplete(installId, step.Name, null);
//recurse
return IterateNextRequiredStep(step, queue, installId);
return IterateNextRequiredStep(step, queue, installId, installModel);
}
//there is no more steps
return string.Empty;
}
/// <summary>
/// Check if the step requires execution
/// </summary>
/// <param name="step"></param>
/// <param name="instruction"></param>
/// <returns></returns>
internal bool StepRequiresExecution(InstallSetupStep step, JToken instruction)
{
var model = instruction == null ? null : instruction.ToObject(step.StepType);
var genericStepType = typeof(InstallSetupStep<>);
Type[] typeArgs = { step.StepType };
var typedStepType = genericStepType.MakeGenericType(typeArgs);
try
{
var method = typedStepType.GetMethods().Single(x => x.Name == "RequiresExecution");
return (bool)method.Invoke(step, new object[] { model });
}
catch (Exception ex)
{
LogHelper.Error<InstallApiController>("Checking if step requires execution (" + step.Name + ") failed.", ex);
throw;
}
}
internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction)
{
using (DisposableTimer.TraceDuration<InstallApiController>("Executing installation step: " + step.Name, "Step completed"))

View File

@@ -37,8 +37,8 @@ namespace Umbraco.Web.Install
{
return new List<InstallSetupStep>
{
new UserStep(_umbContext.Application),
new Upgrade(),
new NewInstallStep(_umbContext.Application),
new UpgradeStep(),
new FilePermissionsStep(),
new MajorVersion7UpgradeReport(_umbContext.Application),
new DatabaseConfigureStep(_umbContext.Application),
@@ -62,13 +62,13 @@ namespace Umbraco.Web.Install
public InstallationType GetInstallationType()
{
return _installationType ?? (_installationType = IsNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value;
return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value;
}
/// <summary>
/// Checks if this is a brand new install meaning that there is no configured version and there is no configured database connection
/// </summary>
private bool IsNewInstall
private bool IsBrandNewInstall
{
get
{
@@ -79,6 +79,34 @@ namespace Umbraco.Web.Install
//no version or conn string configured, must be a brand new install
return true;
}
//now we have to check if this is really a new install, the db might be configured and might contain data
if (_umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false
|| _umbContext.Application.DatabaseContext.IsDatabaseConfigured == false)
{
return true;
}
//check if we have the default user configured already
var result = _umbContext.Application.DatabaseContext.Database.ExecuteScalar<int>(
"SELECT COUNT(*) FROM umbracoUser WHERE id=0 AND userPassword='default'");
if (result == 1)
{
//the user has not been configured
return true;
}
// //check if there are any content types configured, if there isn't then we will consider this a new install
// result = _umbContext.Application.DatabaseContext.Database.ExecuteScalar<int>(
// @"SELECT COUNT(*) FROM cmsContentType
// INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
// WHERE umbracoNode.nodeObjectType = @contentType", new {contentType = Constants.ObjectTypes.DocumentType});
// if (result == 0)
// {
// //no content types have been created
// return true;
// }
return false;
}

View File

@@ -101,7 +101,7 @@ namespace Umbraco.Web.Install.InstallSteps
get { return ShouldDisplayView() ? base.View : ""; }
}
public override bool RequiresExecution()
public override bool RequiresExecution(DatabaseModel model)
{
return ShouldDisplayView();
}

View File

@@ -54,7 +54,7 @@ namespace Umbraco.Web.Install.InstallSteps
}
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
return true;
}

View File

@@ -38,7 +38,7 @@ namespace Umbraco.Web.Install.InstallSteps
return null;
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
//if it's properly configured (i.e. the versions match) then no upgrade necessary
if (_applicationContext.IsConfigured)

View File

@@ -126,7 +126,7 @@ namespace Umbraco.Web.Install.InstallSteps
}
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
return true;
}

View File

@@ -31,7 +31,7 @@ namespace Umbraco.Web.Install.InstallSteps
return new InstallSetupResult("version7upgradereport", CreateReport());
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
//if it's configured, then no need to run
if (_applicationContext.IsConfigured)

View File

@@ -8,14 +8,21 @@ using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
/// <summary>
/// This is the first UI step for a brand new install
/// </summary>
/// <remarks>
/// By default this will show the user view which is the most basic information to configure a new install, but if an install get's interupted because of an
/// error, etc... and the end-user refreshes the installer then we cannot show the user screen because they've already entered that information so instead we'll
/// display a simple continue installation view.
/// </remarks>
[InstallSetupStep(InstallationType.NewInstall,
"User", "user", 20, "Saving your user credentials")]
internal class UserStep : InstallSetupStep<UserModel>
"User", 20, "Saving your user credentials")]
internal class NewInstallStep : InstallSetupStep<UserModel>
{
private readonly ApplicationContext _applicationContext;
public UserStep(ApplicationContext applicationContext)
public NewInstallStep(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
@@ -71,10 +78,14 @@ namespace Umbraco.Web.Install.InstallSteps
public override string View
{
get { return RequiresExecution() ? base.View : string.Empty; }
get { return RequiresExecution(null)
//the user UI
? "user"
//the continue install UI
: "continueinstall"; }
}
public override bool RequiresExecution()
public override bool RequiresExecution(UserModel model)
{
//if there's already a version then there should def be a user
if (GlobalSettings.ConfigurationStatus.IsNullOrWhiteSpace() == false) return false;

View File

@@ -51,7 +51,7 @@ namespace Umbraco.Web.Install.InstallSteps
return null;
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
return true;
}

View File

@@ -42,7 +42,7 @@ namespace Umbraco.Web.Install.InstallSteps
library.RefreshContent();
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
var installSteps = InstallStatusTracker.GetStatus().ToArray();
//this step relies on the preious one completed - because it has stored some information we need

View File

@@ -32,6 +32,12 @@ namespace Umbraco.Web.Install.InstallSteps
var defaultPackageId = modules.First().RepoGuid;
starterKitId = defaultPackageId;
}
else if (starterKitId.Value == Guid.Empty)
{
//if the startkit id is an empty GUID then it means the user has decided not to install one
// so we'll just exit
return null;
}
var result = DownloadPackageFiles(starterKitId.Value);
@@ -78,8 +84,14 @@ namespace Umbraco.Web.Install.InstallSteps
get { return (InstalledPackage.GetAllInstalledPackages().Count > 0) ? string.Empty : base.View; }
}
public override bool RequiresExecution()
public override bool RequiresExecution(Guid? model)
{
//Don't execute if it's an empty GUID - meaning the user has chosen not to install one
if (model.HasValue && model.Value == Guid.Empty)
{
return false;
}
if (InstalledPackage.GetAllInstalledPackages().Count > 0)
return false;

View File

@@ -45,7 +45,7 @@ namespace Umbraco.Web.Install.InstallSteps
installer.InstallBusinessLogic(manifestId, packageFile);
}
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
var installSteps = InstallStatusTracker.GetStatus().ToArray();
//this step relies on the preious one completed - because it has stored some information we need

View File

@@ -7,9 +7,9 @@ namespace Umbraco.Web.Install.InstallSteps
/// </summary>
[InstallSetupStep(InstallationType.Upgrade,
"Upgrade", "upgrade", 1, "Upgrading umbraco")]
internal class Upgrade : InstallSetupStep<object>
internal class UpgradeStep : InstallSetupStep<object>
{
public override bool RequiresExecution()
public override bool RequiresExecution(object model)
{
return true;
}

View File

@@ -25,6 +25,12 @@ namespace Umbraco.Web.Install.Models
/// <param name="model"></param>
/// <returns></returns>
public abstract InstallSetupResult Execute(T model);
/// <summary>
/// Determines if this step needs to execute based on the current state of the application and/or install process
/// </summary>
/// <returns></returns>
public abstract bool RequiresExecution(T model);
}
[DataContract(Name = "step", Namespace = "")]
@@ -60,12 +66,6 @@ namespace Umbraco.Web.Install.Models
[IgnoreDataMember]
public bool PerformsAppRestart { get; private set; }
/// <summary>
/// Determines if this step needs to execute based on the current state of the application and/or install process
/// </summary>
/// <returns></returns>
public abstract bool RequiresExecution();
/// <summary>
/// Defines what order this step needs to execute on the server side since the
/// steps might be shown out of order on the front-end

View File

@@ -313,8 +313,8 @@
<Compile Include="Install\InstallSteps\StarterKitCleanupStep.cs" />
<Compile Include="Install\InstallSteps\StarterKitDownloadStep.cs" />
<Compile Include="Install\InstallSteps\StarterKitInstallStep.cs" />
<Compile Include="Install\InstallSteps\Upgrade.cs" />
<Compile Include="Install\InstallSteps\UserStep.cs" />
<Compile Include="Install\InstallSteps\UpgradeStep.cs" />
<Compile Include="Install\InstallSteps\NewInstallStep.cs" />
<Compile Include="Install\Models\DatabaseModel.cs" />
<Compile Include="Install\Models\DatabaseType.cs" />
<Compile Include="Install\Models\InstallationType.cs" />