2014-02-26 04:15:14 +11:00
|
|
|
|
using System;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
using System.Collections.Generic;
|
2014-02-26 16:01:31 +01:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Net;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
using System.Net.Http;
|
2014-03-04 16:21:45 +11:00
|
|
|
|
using System.Reflection;
|
2014-02-26 16:01:31 +01:00
|
|
|
|
using System.Text;
|
2014-02-26 04:15:14 +11:00
|
|
|
|
using System.Web.Http;
|
2014-02-26 16:01:31 +01:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
using Umbraco.Core;
|
|
|
|
|
|
using Umbraco.Core.Configuration;
|
2014-02-26 16:01:31 +01:00
|
|
|
|
using Umbraco.Core.Logging;
|
|
|
|
|
|
using Umbraco.Core.Persistence;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
using Umbraco.Web.Install.Models;
|
|
|
|
|
|
using Umbraco.Web.WebApi;
|
2014-02-26 04:15:14 +11:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.Install.Controllers
|
|
|
|
|
|
{
|
2014-02-26 18:25:59 +11:00
|
|
|
|
[AngularJsonOnlyConfiguration]
|
2014-02-26 04:15:14 +11:00
|
|
|
|
[HttpInstallAuthorize]
|
|
|
|
|
|
public class InstallApiController : ApiController
|
|
|
|
|
|
{
|
|
|
|
|
|
protected InstallApiController()
|
|
|
|
|
|
: this(UmbracoContext.Current)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected InstallApiController(UmbracoContext umbracoContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
|
|
|
|
|
|
UmbracoContext = umbracoContext;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns the current UmbracoContext
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public UmbracoContext UmbracoContext { get; private set; }
|
|
|
|
|
|
|
2014-02-26 16:01:31 +01:00
|
|
|
|
public ApplicationContext ApplicationContext
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return UmbracoContext.Application; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-04 16:21:45 +11:00
|
|
|
|
private InstallHelper _helper;
|
|
|
|
|
|
internal InstallHelper InstallHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _helper ?? (_helper = new InstallHelper(UmbracoContext));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-26 18:25:59 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the install setup
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public InstallSetup GetSetup()
|
|
|
|
|
|
{
|
2014-03-04 19:20:36 +11:00
|
|
|
|
var setup = new InstallSetup();
|
2014-03-05 10:32:59 +11:00
|
|
|
|
|
2014-02-26 18:25:59 +11:00
|
|
|
|
//TODO: Check for user/site token
|
|
|
|
|
|
|
2014-02-26 16:01:31 +01:00
|
|
|
|
var steps = new List<InstallSetupStep>();
|
2014-02-26 18:25:59 +11:00
|
|
|
|
|
2014-03-04 19:20:36 +11:00
|
|
|
|
var installSteps = InstallHelper.GetStepsForCurrentInstallType().ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
//only get the steps that are targetting the current install type
|
|
|
|
|
|
steps.AddRange(installSteps);
|
2014-03-04 11:16:42 +11:00
|
|
|
|
setup.Steps = steps;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
|
2014-03-04 19:20:36 +11:00
|
|
|
|
InstallStatusTracker.Initialize(setup.InstallId, installSteps);
|
|
|
|
|
|
|
2014-03-04 11:16:42 +11:00
|
|
|
|
return setup;
|
2014-02-26 18:25:59 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Checks if the db can be connected to
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public HttpResponseMessage PostCheckDbConnection()
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Checks if the db credentials are correct
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public HttpResponseMessage PostCheckDbCredentials()
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-03 08:57:00 +01:00
|
|
|
|
public IEnumerable<Package> GetPackages()
|
|
|
|
|
|
{
|
|
|
|
|
|
var r = new org.umbraco.our.Repository();
|
|
|
|
|
|
var modules = r.Modules();
|
2014-03-05 10:32:59 +11:00
|
|
|
|
|
2014-03-04 16:21:45 +11:00
|
|
|
|
return modules.Select(package => new Package()
|
|
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
Id = package.RepoGuid,
|
|
|
|
|
|
Name = package.Text,
|
2014-03-04 16:21:45 +11:00
|
|
|
|
Thumbnail = package.Thumbnail
|
|
|
|
|
|
});
|
2014-03-03 08:57:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-26 18:25:59 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Does the install
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2014-03-05 10:32:59 +11:00
|
|
|
|
public InstallProgressResultModel PostPerformInstall(InstallInstructions installModel)
|
2014-02-26 18:25:59 +11:00
|
|
|
|
{
|
2014-03-04 16:21:45 +11:00
|
|
|
|
if (installModel == null) throw new ArgumentNullException("installModel");
|
2014-02-26 18:25:59 +11:00
|
|
|
|
|
2014-03-04 16:21:45 +11:00
|
|
|
|
var status = InstallStatusTracker.GetStatus().ToArray();
|
2014-03-04 19:20:36 +11:00
|
|
|
|
//there won't be any statuses returned if the app pool has restarted so we need to re-read from file.
|
2014-03-04 16:21:45 +11:00
|
|
|
|
if (status.Any() == false)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-04 19:20:36 +11:00
|
|
|
|
status = InstallStatusTracker.InitializeFromFile(installModel.InstallId).ToArray();
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
//var queue = new Queue<InstallTrackingItem>(status);
|
|
|
|
|
|
//while (queue.Count > 0)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// var stepStatus = queue.Dequeue();
|
|
|
|
|
|
|
|
|
|
|
|
// //if it is not complete, then we need to execute it
|
|
|
|
|
|
// if (stepStatus.IsComplete == false)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// var step = InstallHelper.GetAllSteps().Single(x => x.Name == stepStatus.Name);
|
|
|
|
|
|
|
|
|
|
|
|
// JToken instruction = null;
|
|
|
|
|
|
// if (step.HasUIElement)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// //Since this is a UI instruction, we will extract the model from it
|
|
|
|
|
|
// if (installModel.Instructions.Any(x => x.Key == step.Name) == false)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// throw new HttpResponseException(Request.CreateValidationErrorResponse("No instruction defined for step: " + step.Name));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// instruction = installModel.Instructions[step.Name];
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// //If this step doesn't require execution then continue to the next one.
|
|
|
|
|
|
// if (step.RequiresExecution() == false)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// //set this as complete and continue
|
|
|
|
|
|
// InstallStatusTracker.SetComplete(installModel.InstallId, stepStatus.Name, null);
|
|
|
|
|
|
// continue;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// try
|
|
|
|
|
|
// {
|
|
|
|
|
|
// var setupData = ExecuteStep(step, instruction);
|
|
|
|
|
|
|
|
|
|
|
|
// //update the status
|
|
|
|
|
|
// InstallStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData != null ? setupData.SavedStepData : null);
|
|
|
|
|
|
|
|
|
|
|
|
// //get the next step if there is one
|
|
|
|
|
|
|
|
|
|
|
|
// var nextStep = "";
|
|
|
|
|
|
// if ((index + 1) < status.Length)
|
|
|
|
|
|
// {
|
|
|
|
|
|
|
|
|
|
|
|
// nextStep = status[index + 1].Name;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// //check if there's a custom view to return for this step
|
|
|
|
|
|
// if (setupData != null && setupData.View.IsNullOrWhiteSpace() == false)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return new InstallProgressResultModel(false, step.Name, nextStep, setupData.View, setupData.ViewModel);
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// return new InstallProgressResultModel(false, step.Name, nextStep);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// catch (Exception ex)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// if (ex is TargetInvocationException && ex.InnerException != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// ex = ex.InnerException;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// var installException = ex as InstallException;
|
|
|
|
|
|
// if (installException != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// throw new HttpResponseException(Request.CreateValidationErrorResponse(new
|
|
|
|
|
|
// {
|
|
|
|
|
|
// view = installException.View,
|
|
|
|
|
|
// model = installException.ViewModel,
|
|
|
|
|
|
// message = installException.Message
|
|
|
|
|
|
// }));
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// throw new HttpResponseException(
|
|
|
|
|
|
// Request.CreateValidationErrorResponse("An error occurred executing the step: " + step.Name + ". Error: " + ex.Message));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
for (var index = 0; index < status.Length; index++)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
var stepStatus = status[index];
|
2014-02-26 16:01:31 +01:00
|
|
|
|
//if it is not complete, then we need to execute it
|
2014-03-04 16:21:45 +11:00
|
|
|
|
if (stepStatus.IsComplete == false)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-04 19:20:36 +11:00
|
|
|
|
var step = InstallHelper.GetAllSteps().Single(x => x.Name == stepStatus.Name);
|
2014-03-04 16:21:45 +11:00
|
|
|
|
|
2014-02-26 16:01:31 +01:00
|
|
|
|
JToken instruction = null;
|
2014-03-04 11:16:42 +11:00
|
|
|
|
if (step.HasUIElement)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
|
|
|
|
|
//Since this is a UI instruction, we will extract the model from it
|
2014-03-04 16:21:45 +11:00
|
|
|
|
if (installModel.Instructions.Any(x => x.Key == step.Name) == false)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
throw new HttpResponseException(Request.CreateValidationErrorResponse("No instruction defined for step: " + step.Name));
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
2014-03-04 16:21:45 +11:00
|
|
|
|
instruction = installModel.Instructions[step.Name];
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
2014-03-04 11:16:42 +11:00
|
|
|
|
|
|
|
|
|
|
//If this step doesn't require execution then continue to the next one.
|
|
|
|
|
|
if (step.RequiresExecution() == false)
|
|
|
|
|
|
{
|
2014-03-04 16:21:45 +11:00
|
|
|
|
//set this as complete and continue
|
2014-03-05 10:32:59 +11:00
|
|
|
|
InstallStatusTracker.SetComplete(installModel.InstallId, stepStatus.Name, null);
|
2014-03-04 11:16:42 +11:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-26 16:01:31 +01:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var setupData = ExecuteStep(step, instruction);
|
|
|
|
|
|
|
|
|
|
|
|
//update the status
|
2014-03-04 16:21:45 +11:00
|
|
|
|
InstallStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData != null ? setupData.SavedStepData : null);
|
|
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
//get the next step if there is one
|
|
|
|
|
|
var nextStep = "";
|
|
|
|
|
|
if ((index + 1) < status.Length)
|
2014-03-04 16:21:45 +11:00
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
|
|
|
|
|
|
nextStep = status[index + 1].Name;
|
2014-03-04 16:21:45 +11:00
|
|
|
|
}
|
2014-03-03 08:57:00 +01:00
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
//check if there's a custom view to return for this step
|
|
|
|
|
|
if (setupData != null && setupData.View.IsNullOrWhiteSpace() == false)
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
return new InstallProgressResultModel(false, step.Name, nextStep, setupData.View, setupData.ViewModel);
|
|
|
|
|
|
}
|
2014-03-04 16:21:45 +11:00
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
return new InstallProgressResultModel(false, step.Name, nextStep);
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2014-03-04 16:21:45 +11:00
|
|
|
|
if (ex is TargetInvocationException && ex.InnerException != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ex = ex.InnerException;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-04 19:11:27 +01:00
|
|
|
|
//return custom view if we have an install exception
|
2014-03-04 16:21:45 +11:00
|
|
|
|
var installException = ex as InstallException;
|
|
|
|
|
|
if (installException != null)
|
|
|
|
|
|
{
|
2014-03-05 10:32:59 +11:00
|
|
|
|
throw new HttpResponseException(Request.CreateValidationErrorResponse(new
|
2014-03-04 16:21:45 +11:00
|
|
|
|
{
|
2014-03-04 19:11:27 +01:00
|
|
|
|
step = step.Name,
|
2014-03-04 16:21:45 +11:00
|
|
|
|
view = installException.View,
|
|
|
|
|
|
model = installException.ViewModel,
|
|
|
|
|
|
message = installException.Message
|
2014-03-05 10:32:59 +11:00
|
|
|
|
}));
|
2014-03-04 16:21:45 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
throw new HttpResponseException(
|
|
|
|
|
|
Request.CreateValidationErrorResponse("An error occurred executing the step: " + step.Name + ". Error: " + ex.Message));
|
2014-03-04 19:11:27 +01:00
|
|
|
|
//return standard view + step and message to display generic message
|
|
|
|
|
|
return Json(new
|
|
|
|
|
|
{
|
|
|
|
|
|
step = step.Name,
|
|
|
|
|
|
view = "error",
|
|
|
|
|
|
message = ex.Message
|
|
|
|
|
|
}, HttpStatusCode.BadRequest);
|
|
|
|
|
|
|
|
|
|
|
|
//return Request.CreateValidationErrorResponse("An error occurred executing the step: " + step.Name + ". Error: " + ex.Message);
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstallStatusTracker.Reset();
|
2014-03-05 10:32:59 +11:00
|
|
|
|
return new InstallProgressResultModel(true, "", "");
|
2014-02-26 16:01:31 +01:00
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
//return Json(new { complete = true }, HttpStatusCode.OK);
|
2014-02-26 18:25:59 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-05 10:32:59 +11:00
|
|
|
|
//private InstallSetupStep IterateNextRequiredStep(Queue<InstallTrackingItem> queue)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// var step = InstallHelper.GetAllSteps().Single(x => x.Name == stepStatus.Name);
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
2014-03-04 11:16:42 +11:00
|
|
|
|
internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction)
|
2014-02-26 18:25:59 +11:00
|
|
|
|
{
|
2014-03-04 16:21:45 +11:00
|
|
|
|
using (DisposableTimer.TraceDuration<InstallApiController>("Executing installation step: " + step.Name, "Step completed"))
|
2014-02-26 16:01:31 +01:00
|
|
|
|
{
|
2014-03-04 16:21:45 +11:00
|
|
|
|
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 == "Execute");
|
|
|
|
|
|
return (InstallSetupResult)method.Invoke(step, new object[] { model });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.Error<InstallApiController>("Installation step " + step.Name + " failed.", ex);
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
2014-02-26 16:01:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-03-05 10:32:59 +11:00
|
|
|
|
|
2014-02-26 16:01:31 +01:00
|
|
|
|
private HttpResponseMessage Json(object jsonObject, HttpStatusCode status)
|
|
|
|
|
|
{
|
|
|
|
|
|
var response = Request.CreateResponse(status);
|
|
|
|
|
|
var json = JObject.FromObject(jsonObject);
|
|
|
|
|
|
response.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json");
|
|
|
|
|
|
return response;
|
|
|
|
|
|
}
|
2014-03-05 10:32:59 +11:00
|
|
|
|
|
2014-02-26 04:15:14 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|