Implemented Initializing status.

This commit is contained in:
Sipke Schoorstra
2015-08-23 18:27:36 +01:00
parent b560a4c3d0
commit 81adb78818
17 changed files with 161 additions and 116 deletions

View File

@@ -91,6 +91,7 @@
<Compile Include="Providers\Builders\ContentStep.cs" /> <Compile Include="Providers\Builders\ContentStep.cs" />
<Compile Include="Providers\Builders\RecipeMetadataStep.cs" /> <Compile Include="Providers\Builders\RecipeMetadataStep.cs" />
<Compile Include="Providers\Builders\SettingsStep.cs" /> <Compile Include="Providers\Builders\SettingsStep.cs" />
<Compile Include="Providers\Executors\ActivateShellStep.cs" />
<Compile Include="Providers\Executors\ActivateSweepGeneratorStep.cs" /> <Compile Include="Providers\Executors\ActivateSweepGeneratorStep.cs" />
<Compile Include="Providers\Executors\CommandStep.cs" /> <Compile Include="Providers\Executors\CommandStep.cs" />
<Compile Include="Providers\Executors\RemoveContentStep.cs" /> <Compile Include="Providers\Executors\RemoveContentStep.cs" />

View File

@@ -0,0 +1,23 @@
using Orchard.Environment.Configuration;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
namespace Orchard.Recipes.Providers.Executors {
public class ActivateShellStep : RecipeExecutionStep {
private readonly ShellSettings _shellSettings;
private readonly IShellSettingsManager _shellSettingsManager;
public ActivateShellStep(ShellSettings shellSettings, IShellSettingsManager shellSettingsManager, RecipeExecutionLogger logger)
: base(logger) {
_shellSettings = shellSettings;
_shellSettingsManager = shellSettingsManager;
}
public override string Name { get { return "ActivateShell"; } }
public override void Execute(RecipeExecutionContext context) {
_shellSettings.State = TenantState.Running;
_shellSettingsManager.SaveSettings(_shellSettings);
}
}
}

View File

@@ -74,7 +74,13 @@ namespace Orchard.Recipes.Services {
if (!String.IsNullOrWhiteSpace(recipeName)) if (!String.IsNullOrWhiteSpace(recipeName))
query = from record in query where record.RecipeName == recipeName select record; query = from record in query where record.RecipeName == recipeName select record;
var stepResultRecord = query.Single(); var stepResultRecord = query.SingleOrDefault();
if (stepResultRecord == null)
// No step result record was created when scheduling the step, so simply ignore.
// The only reason where one would not create such a record would be Setup,
// when no database exists to store the record but still wants to schedule a recipe step (such as the "StopViewsBackgroundCompilationStep").
return;
stepResultRecord.IsCompleted = true; stepResultRecord.IsCompleted = true;
stepResultRecord.IsSuccessful = isSuccessful; stepResultRecord.IsSuccessful = isSuccessful;

View File

@@ -24,6 +24,7 @@ namespace Orchard.Setup.Controllers {
ISetupService setupService, ISetupService setupService,
IViewsBackgroundCompilation viewsBackgroundCompilation, IViewsBackgroundCompilation viewsBackgroundCompilation,
ShellSettings shellSettings) { ShellSettings shellSettings) {
_viewsBackgroundCompilation = viewsBackgroundCompilation; _viewsBackgroundCompilation = viewsBackgroundCompilation;
_shellSettings = shellSettings; _shellSettings = shellSettings;
_notifier = notifier; _notifier = notifier;
@@ -42,6 +43,10 @@ namespace Orchard.Setup.Controllers {
public ActionResult Index() { public ActionResult Index() {
var initialSettings = _setupService.Prime(); var initialSettings = _setupService.Prime();
if(initialSettings.State == TenantState.Initializing)
return View("Initializing");
var recipes = _setupService.Recipes().ToList(); var recipes = _setupService.Recipes().ToList();
string recipeDescription = null; string recipeDescription = null;
if (recipes.Count > 0) { if (recipes.Count > 0) {
@@ -59,7 +64,7 @@ namespace Orchard.Setup.Controllers {
return IndexViewResult(new SetupViewModel { return IndexViewResult(new SetupViewModel {
AdminUsername = "admin", AdminUsername = "admin",
DatabaseIsPreconfigured = !string.IsNullOrEmpty(initialSettings.DataProvider), DatabaseIsPreconfigured = !String.IsNullOrEmpty(initialSettings.DataProvider),
Recipes = recipes, Recipes = recipes,
RecipeDescription = recipeDescription RecipeDescription = recipeDescription
}); });
@@ -76,17 +81,17 @@ namespace Orchard.Setup.Controllers {
if (model.DatabaseProvider != SetupDatabaseType.Builtin && string.IsNullOrEmpty(model.DatabaseConnectionString)) if (model.DatabaseProvider != SetupDatabaseType.Builtin && string.IsNullOrEmpty(model.DatabaseConnectionString))
ModelState.AddModelError("DatabaseConnectionString", T("A connection string is required.").Text); ModelState.AddModelError("DatabaseConnectionString", T("A connection string is required.").Text);
if (!string.IsNullOrWhiteSpace(model.ConfirmPassword) && model.AdminPassword != model.ConfirmPassword ) { if (!String.IsNullOrWhiteSpace(model.ConfirmPassword) && model.AdminPassword != model.ConfirmPassword) {
ModelState.AddModelError("ConfirmPassword", T("Password confirmation must match.").Text); ModelState.AddModelError("ConfirmPassword", T("Password confirmation must match.").Text);
} }
if (model.DatabaseProvider != SetupDatabaseType.Builtin && !string.IsNullOrWhiteSpace(model.DatabaseTablePrefix)) { if (model.DatabaseProvider != SetupDatabaseType.Builtin && !string.IsNullOrWhiteSpace(model.DatabaseTablePrefix)) {
model.DatabaseTablePrefix = model.DatabaseTablePrefix.Trim(); model.DatabaseTablePrefix = model.DatabaseTablePrefix.Trim();
if(!char.IsLetter(model.DatabaseTablePrefix[0])) { if (!Char.IsLetter(model.DatabaseTablePrefix[0])) {
ModelState.AddModelError("DatabaseTablePrefix", T("The table prefix must begin with a letter.").Text); ModelState.AddModelError("DatabaseTablePrefix", T("The table prefix must begin with a letter.").Text);
} }
if(model.DatabaseTablePrefix.Any(x => !Char.IsLetterOrDigit(x))) { if (model.DatabaseTablePrefix.Any(x => !Char.IsLetterOrDigit(x))) {
ModelState.AddModelError("DatabaseTablePrefix", T("The table prefix must contain letters or digits.").Text); ModelState.AddModelError("DatabaseTablePrefix", T("The table prefix must contain letters or digits.").Text);
} }
} }
@@ -103,7 +108,7 @@ namespace Orchard.Setup.Controllers {
foreach (var recipe in recipes.Where(recipe => recipe.Name == model.Recipe)) { foreach (var recipe in recipes.Where(recipe => recipe.Name == model.Recipe)) {
model.RecipeDescription = recipe.Description; model.RecipeDescription = recipe.Description;
} }
model.DatabaseIsPreconfigured = !string.IsNullOrEmpty(_setupService.Prime().DataProvider); model.DatabaseIsPreconfigured = !String.IsNullOrEmpty(_setupService.Prime().DataProvider);
return IndexViewResult(model); return IndexViewResult(model);
} }
@@ -111,8 +116,7 @@ namespace Orchard.Setup.Controllers {
try { try {
string providerName = null; string providerName = null;
switch (model.DatabaseProvider) switch (model.DatabaseProvider) {
{
case SetupDatabaseType.Builtin: case SetupDatabaseType.Builtin:
providerName = "SqlCe"; providerName = "SqlCe";
break; break;
@@ -140,11 +144,11 @@ namespace Orchard.Setup.Controllers {
DatabaseProvider = providerName, DatabaseProvider = providerName,
DatabaseConnectionString = model.DatabaseConnectionString, DatabaseConnectionString = model.DatabaseConnectionString,
DatabaseTablePrefix = model.DatabaseTablePrefix, DatabaseTablePrefix = model.DatabaseTablePrefix,
EnabledFeatures = null, // default list EnabledFeatures = null, // Default list
Recipe = model.Recipe Recipe = model.Recipe
}; };
string executionId = _setupService.Setup(setupContext); var executionId = _setupService.Setup(setupContext);
// First time installation if finally done. Tell the background views compilation // First time installation if finally done. Tell the background views compilation
// process to stop, so that it doesn't interfere with the user (asp.net compilation // process to stop, so that it doesn't interfere with the user (asp.net compilation
@@ -153,7 +157,8 @@ namespace Orchard.Setup.Controllers {
// Redirect to the welcome page. // Redirect to the welcome page.
return Redirect("~/" + _shellSettings.RequestUrlPrefix); return Redirect("~/" + _shellSettings.RequestUrlPrefix);
} catch (Exception ex) { }
catch (Exception ex) {
Logger.Error(ex, "Setup failed"); Logger.Error(ex, "Setup failed");
_notifier.Error(T("Setup failed: {0}", ex.Message)); _notifier.Error(T("Setup failed: {0}", ex.Message));
@@ -161,7 +166,7 @@ namespace Orchard.Setup.Controllers {
foreach (var recipe in recipes.Where(recipe => recipe.Name == model.Recipe)) { foreach (var recipe in recipes.Where(recipe => recipe.Name == model.Recipe)) {
model.RecipeDescription = recipe.Description; model.RecipeDescription = recipe.Description;
} }
model.DatabaseIsPreconfigured = !string.IsNullOrEmpty(_setupService.Prime().DataProvider); model.DatabaseIsPreconfigured = !String.IsNullOrEmpty(_setupService.Prime().DataProvider);
return IndexViewResult(model); return IndexViewResult(model);
} }

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Orchard.Setup.Controllers
{
public enum SetupDatabaseType
{
Builtin,
SqlServer,
MySql,
PostgreSql,
}
}

View File

@@ -25,6 +25,7 @@
<IISExpressAnonymousAuthentication /> <IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication /> <IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode /> <IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@@ -78,7 +79,7 @@
<Compile Include="Annotations\StringLengthMin.cs" /> <Compile Include="Annotations\StringLengthMin.cs" />
<Compile Include="Commands\SetupCommand.cs" /> <Compile Include="Commands\SetupCommand.cs" />
<Compile Include="Controllers\SetupController.cs" /> <Compile Include="Controllers\SetupController.cs" />
<Compile Include="Controllers\SetupDatabaseType.cs" /> <Compile Include="ViewModels\SetupDatabaseType.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routes.cs" /> <Compile Include="Routes.cs" />
<Compile Include="Services\ISetupService.cs" /> <Compile Include="Services\ISetupService.cs" />
@@ -136,6 +137,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Document.cshtml" /> <Content Include="Views\Document.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Setup\Initializing.cshtml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -4,8 +4,7 @@ using System.Web.Routing;
using Orchard.Mvc.Routes; using Orchard.Mvc.Routes;
namespace Orchard.Setup { namespace Orchard.Setup {
public class Routes : IRouteProvider public class Routes : IRouteProvider {
{
public void GetRoutes(ICollection<RouteDescriptor> routes) { public void GetRoutes(ICollection<RouteDescriptor> routes) {
foreach (var routeDescriptor in GetRoutes()) foreach (var routeDescriptor in GetRoutes())
routes.Add(routeDescriptor); routes.Add(routeDescriptor);

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Web; using System.Web;
using System.Xml.Linq;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Core.Settings.Models; using Orchard.Core.Settings.Models;
using Orchard.Data; using Orchard.Data;
@@ -16,7 +17,6 @@ using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.Environment.ShellBuilders; using Orchard.Environment.ShellBuilders;
using Orchard.Environment.State; using Orchard.Environment.State;
using Orchard.Localization;
using Orchard.Localization.Services; using Orchard.Localization.Services;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Recipes.Models; using Orchard.Recipes.Models;
@@ -26,7 +26,7 @@ using Orchard.Settings;
using Orchard.Utility.Extensions; using Orchard.Utility.Extensions;
namespace Orchard.Setup.Services { namespace Orchard.Setup.Services {
public class SetupService : ISetupService { public class SetupService : Component, ISetupService {
private readonly ShellSettings _shellSettings; private readonly ShellSettings _shellSettings;
private readonly IOrchardHost _orchardHost; private readonly IOrchardHost _orchardHost;
private readonly IShellSettingsManager _shellSettingsManager; private readonly IShellSettingsManager _shellSettingsManager;
@@ -46,6 +46,7 @@ namespace Orchard.Setup.Services {
IProcessingEngine processingEngine, IProcessingEngine processingEngine,
IExtensionManager extensionManager, IExtensionManager extensionManager,
IRecipeHarvester recipeHarvester) { IRecipeHarvester recipeHarvester) {
_shellSettings = shellSettings; _shellSettings = shellSettings;
_orchardHost = orchardHost; _orchardHost = orchardHost;
_shellSettingsManager = shellSettingsManager; _shellSettingsManager = shellSettingsManager;
@@ -54,13 +55,8 @@ namespace Orchard.Setup.Services {
_processingEngine = processingEngine; _processingEngine = processingEngine;
_extensionManager = extensionManager; _extensionManager = extensionManager;
_recipeHarvester = recipeHarvester; _recipeHarvester = recipeHarvester;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
} }
public Localizer T { get; set; }
public ILogger Logger { get; set; }
public ShellSettings Prime() { public ShellSettings Prime() {
return _shellSettings; return _shellSettings;
} }
@@ -99,23 +95,20 @@ namespace Orchard.Setup.Services {
var shellSettings = new ShellSettings(_shellSettings); var shellSettings = new ShellSettings(_shellSettings);
if (string.IsNullOrEmpty(shellSettings.DataProvider)) { if (String.IsNullOrEmpty(shellSettings.DataProvider)) {
shellSettings.DataProvider = context.DatabaseProvider; shellSettings.DataProvider = context.DatabaseProvider;
shellSettings.DataConnectionString = context.DatabaseConnectionString; shellSettings.DataConnectionString = context.DatabaseConnectionString;
shellSettings.DataTablePrefix = context.DatabaseTablePrefix; shellSettings.DataTablePrefix = context.DatabaseTablePrefix;
} }
#region Encryption Settings
shellSettings.EncryptionAlgorithm = "AES"; shellSettings.EncryptionAlgorithm = "AES";
// randomly generated key
// Randomly generated key.
shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString(); shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString();
shellSettings.HashAlgorithm = "HMACSHA256"; shellSettings.HashAlgorithm = "HMACSHA256";
// randomly generated key
shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString();
#endregion // Randomly generated key.
shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString();
var shellDescriptor = new ShellDescriptor { var shellDescriptor = new ShellDescriptor {
Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
@@ -123,12 +116,12 @@ namespace Orchard.Setup.Services {
var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor); var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
// initialize database explicitly, and store shell descriptor // Initialize database explicitly, and store shell descriptor.
using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) { using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) {
using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) { using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) {
// check if the database is already created (in case an exception occured in the second phase) // Check if the database is already created (in case an exception occured in the second phase).
var schemaBuilder = new SchemaBuilder(environment.Resolve<IDataMigrationInterpreter>()); var schemaBuilder = new SchemaBuilder(environment.Resolve<IDataMigrationInterpreter>());
var installationPresent = true; var installationPresent = true;
try { try {
@@ -143,7 +136,7 @@ namespace Orchard.Setup.Services {
throw new OrchardException(T("A previous Orchard installation was detected in this database with this table prefix.")); throw new OrchardException(T("A previous Orchard installation was detected in this database with this table prefix."));
} }
// Make a workaround to avoid the Transaction issue for PostgreSQL // Workaround to avoid some Transaction issue for PostgreSQL.
environment.Resolve<ITransactionManager>().RequireNew(); environment.Resolve<ITransactionManager>().RequireNew();
schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table
@@ -168,15 +161,15 @@ namespace Orchard.Setup.Services {
} }
} }
// in effect "pump messages" see PostMessage circa 1980 // In effect "pump messages" see PostMessage circa 1980.
while (_processingEngine.AreTasksPending()) while (_processingEngine.AreTasksPending())
_processingEngine.ExecuteNextTask(); _processingEngine.ExecuteNextTask();
// creating a standalone environment. // Creating a standalone environment.
// in theory this environment can be used to resolve any normal components by interface, and those // in theory this environment can be used to resolve any normal components by interface, and those
// components will exist entirely in isolation - no crossover between the safemode container currently in effect // components will exist entirely in isolation - no crossover between the safemode container currently in effect.
// must mark state as Running - otherwise standalone enviro is created "for setup" // Set shell state to "Running" so that the proper shell context is created.
shellSettings.State = TenantState.Running; shellSettings.State = TenantState.Running;
using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) {
try { try {
@@ -188,24 +181,25 @@ namespace Orchard.Setup.Services {
} }
} }
// Set shell state to "Initializing" so that subsequent HTTP requests are responded to with "Service Unavailable" while Orchard is setting up.
shellSettings.State = _shellSettings.State = TenantState.Initializing;
_shellSettingsManager.SaveSettings(shellSettings); _shellSettingsManager.SaveSettings(shellSettings);
return executionId; return executionId;
} }
private string CreateTenantData(SetupContext context, IWorkContextScope environment) { private string CreateTenantData(SetupContext context, IWorkContextScope environment) {
// create superuser // Create site owner.
var membershipService = environment.Resolve<IMembershipService>(); var membershipService = environment.Resolve<IMembershipService>();
var user = var user = membershipService.CreateUser(
membershipService.CreateUser(new CreateUserParams(context.AdminUsername, context.AdminPassword, new CreateUserParams(context.AdminUsername, context.AdminPassword,
String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty, true));
true));
// set superuser as current user for request (it will be set as the owner of all content items) // Set site owner as current user for request (it will be set as the owner of all content items).
var authenticationService = environment.Resolve<IAuthenticationService>(); var authenticationService = environment.Resolve<IAuthenticationService>();
authenticationService.SetAuthenticatedUserForRequest(user); authenticationService.SetAuthenticatedUserForRequest(user);
// set site name and settings // Set site name and settings.
var siteService = environment.Resolve<ISiteService>(); var siteService = environment.Resolve<ISiteService>();
var siteSettings = siteService.GetSiteSettings().As<SiteSettingsPart>(); var siteSettings = siteService.GetSiteSettings().As<SiteSettingsPart>();
siteSettings.SiteSalt = Guid.NewGuid().ToString("N"); siteSettings.SiteSalt = Guid.NewGuid().ToString("N");
@@ -213,7 +207,7 @@ namespace Orchard.Setup.Services {
siteSettings.SuperUser = context.AdminUsername; siteSettings.SuperUser = context.AdminUsername;
siteSettings.SiteCulture = "en-US"; siteSettings.SiteCulture = "en-US";
// add default culture // Add default culture.
var cultureManager = environment.Resolve<ICultureManager>(); var cultureManager = environment.Resolve<ICultureManager>();
cultureManager.AddCulture("en-US"); cultureManager.AddCulture("en-US");
@@ -225,7 +219,19 @@ namespace Orchard.Setup.Services {
var executionId = recipeManager.Execute(recipe); var executionId = recipeManager.Execute(recipe);
// null check: temporary fix for running setup in command line // Once the recipe has finished executing, we need to update the shell state to "Running", so add a recipe step that does exactly that.
var recipeStepQueue = environment.Resolve<IRecipeStepQueue>();
var recipeStepResultRecordRepository = environment.Resolve<IRepository<RecipeStepResultRecord>>();
var activateShellStep = new RecipeStep(Guid.NewGuid().ToString("N"), recipe.Name, "ActivateShell", new XElement("ActivateShell"));
recipeStepQueue.Enqueue(executionId, activateShellStep);
recipeStepResultRecordRepository.Create(new RecipeStepResultRecord {
ExecutionId = executionId,
RecipeName = recipe.Name,
StepId = activateShellStep.Id,
StepName = activateShellStep.Name
});
// Null check: temporary fix for running setup in command line.
if (HttpContext.Current != null) { if (HttpContext.Current != null) {
authenticationService.SignIn(user, true); authenticationService.SignIn(user, true);
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Web.Routing; using System.Web.Routing;
using Autofac; using Autofac;
using Orchard.Caching; using Orchard.Caching;

View File

@@ -0,0 +1,10 @@
namespace Orchard.Setup.ViewModels
{
public enum SetupDatabaseType
{
Builtin,
SqlServer,
MySql,
PostgreSql,
}
}

View File

@@ -1,5 +1,6 @@
@model Orchard.Setup.ViewModels.SetupViewModel @model Orchard.Setup.ViewModels.SetupViewModel
@using Orchard.Recipes.Models; @using Orchard.Recipes.Models;
@using Orchard.Setup.ViewModels
@{ @{
Script.Require("jQuery"); Script.Require("jQuery");
Script.Require("ShapesBase"); Script.Require("ShapesBase");
@@ -47,19 +48,19 @@ if (!Model.DatabaseIsPreconfigured) {
<legend>@T("How would you like to store your data?")</legend> <legend>@T("How would you like to store your data?")</legend>
@Html.ValidationMessage("DatabaseOptions", "Unable to setup data storage.") @Html.ValidationMessage("DatabaseOptions", "Unable to setup data storage.")
<div> <div>
@Html.RadioButtonFor(svm => svm.DatabaseProvider, Orchard.Setup.Controllers.SetupDatabaseType.Builtin.ToString(), new { id = "builtin" }) @Html.RadioButtonFor(svm => svm.DatabaseProvider, SetupDatabaseType.Builtin.ToString(), new { id = "builtin" })
<label for="builtin" class="forcheckbox">@T("Use built-in data storage (SQL Server Compact)")</label> <label for="builtin" class="forcheckbox">@T("Use built-in data storage (SQL Server Compact)")</label>
</div> </div>
<div> <div>
@Html.RadioButtonFor(svm => svm.DatabaseProvider, Orchard.Setup.Controllers.SetupDatabaseType.SqlServer.ToString(), new { id = "sqlserver" }) @Html.RadioButtonFor(svm => svm.DatabaseProvider, SetupDatabaseType.SqlServer.ToString(), new { id = "sqlserver" })
<label for="sqlserver" class="forcheckbox">@T("Use an existing SQL Server, SQL Express database")</label> <label for="sqlserver" class="forcheckbox">@T("Use an existing SQL Server, SQL Express database")</label>
</div> </div>
<div> <div>
@Html.RadioButtonFor(svm => svm.DatabaseProvider, Orchard.Setup.Controllers.SetupDatabaseType.MySql.ToString(), new { id = "mysql" }) @Html.RadioButtonFor(svm => svm.DatabaseProvider, SetupDatabaseType.MySql.ToString(), new { id = "mysql" })
<label for="mysql" class="forcheckbox">@T("Use an existing MySql database")</label> <label for="mysql" class="forcheckbox">@T("Use an existing MySql database")</label>
</div> </div>
<div> <div>
@Html.RadioButtonFor(svm => svm.DatabaseProvider, Orchard.Setup.Controllers.SetupDatabaseType.PostgreSql.ToString(), new { id = "postgresql" }) @Html.RadioButtonFor(svm => svm.DatabaseProvider, SetupDatabaseType.PostgreSql.ToString(), new { id = "postgresql" })
<label for="postgresql" class="forcheckbox">@T("Use an existing PostgreSQL database")</label> <label for="postgresql" class="forcheckbox">@T("Use an existing PostgreSQL database")</label>
</div> </div>
<div data-controllerid="builtin" data-defaultstate="hidden"> <div data-controllerid="builtin" data-defaultstate="hidden">

View File

@@ -0,0 +1 @@
@T("Orchard is initializing. Please check back in a few minutes.")

View File

@@ -9,7 +9,7 @@ namespace Orchard.Environment.Configuration {
public class ShellSettingsManager : Component, IShellSettingsManager { public class ShellSettingsManager : Component, IShellSettingsManager {
private const string _settingsFileName = "Settings.txt"; private const string SettingsFileName = "Settings.txt";
private readonly IAppDataFolder _appDataFolder; private readonly IAppDataFolder _appDataFolder;
private readonly IShellSettingsManagerEventHandler _events; private readonly IShellSettingsManagerEventHandler _events;
@@ -37,7 +37,7 @@ namespace Orchard.Environment.Configuration {
throw new ArgumentException("The Name property of the supplied ShellSettings object is null or empty; the settings cannot be saved.", "settings"); throw new ArgumentException("The Name property of the supplied ShellSettings object is null or empty; the settings cannot be saved.", "settings");
Logger.Information("Saving ShellSettings for tenant '{0}'...", settings.Name); Logger.Information("Saving ShellSettings for tenant '{0}'...", settings.Name);
var filePath = Path.Combine(Path.Combine("Sites", settings.Name), _settingsFileName); var filePath = Path.Combine(Path.Combine("Sites", settings.Name), SettingsFileName);
_appDataFolder.CreateFile(filePath, ShellSettingsSerializer.ComposeSettings(settings)); _appDataFolder.CreateFile(filePath, ShellSettingsSerializer.ComposeSettings(settings));
Logger.Information("ShellSettings saved successfully; flagging tenant '{0}' for restart.", settings.Name); Logger.Information("ShellSettings saved successfully; flagging tenant '{0}' for restart.", settings.Name);
@@ -48,7 +48,7 @@ namespace Orchard.Environment.Configuration {
var filePaths = _appDataFolder var filePaths = _appDataFolder
.ListDirectories("Sites") .ListDirectories("Sites")
.SelectMany(path => _appDataFolder.ListFiles(path)) .SelectMany(path => _appDataFolder.ListFiles(path))
.Where(path => String.Equals(Path.GetFileName(path), _settingsFileName, StringComparison.OrdinalIgnoreCase)); .Where(path => String.Equals(Path.GetFileName(path), SettingsFileName, StringComparison.OrdinalIgnoreCase));
foreach (var filePath in filePaths) { foreach (var filePath in filePaths) {
yield return ShellSettingsSerializer.ParseSettings(_appDataFolder.ReadFile(filePath)); yield return ShellSettingsSerializer.ParseSettings(_appDataFolder.ReadFile(filePath));

View File

@@ -1,6 +1,7 @@
namespace Orchard.Environment.Configuration { namespace Orchard.Environment.Configuration {
public enum TenantState { public enum TenantState {
Uninitialized, Uninitialized,
Initializing,
Running, Running,
Disabled, Disabled,
Invalid Invalid

View File

@@ -128,12 +128,12 @@ namespace Orchard.Environment {
void CreateAndActivateShells() { void CreateAndActivateShells() {
Logger.Information("Start creation of shells"); Logger.Information("Start creation of shells");
// is there any tenant right now ? // Is there any tenant right now?
var allSettings = _shellSettingsManager.LoadSettings() var allSettings = _shellSettingsManager.LoadSettings()
.Where(settings => settings.State == TenantState.Running || settings.State == TenantState.Uninitialized) .Where(settings => settings.State == TenantState.Running || settings.State == TenantState.Uninitialized || settings.State == TenantState.Initializing)
.ToArray(); .ToArray();
// load all tenants, and activate their shell // Load all tenants, and activate their shell.
if (allSettings.Any()) { if (allSettings.Any()) {
Parallel.ForEach(allSettings, settings => { Parallel.ForEach(allSettings, settings => {
try { try {
@@ -145,7 +145,7 @@ namespace Orchard.Environment {
} }
}); });
} }
// no settings, run the Setup // No settings, run the Setup.
else { else {
var setupContext = CreateSetupContext(); var setupContext = CreateSetupContext();
ActivateShell(setupContext); ActivateShell(setupContext);
@@ -172,25 +172,27 @@ namespace Orchard.Environment {
} }
/// <summary> /// <summary>
/// Creates a transient shell for the default tenant's setup /// Creates a transient shell for the default tenant's setup.
/// </summary> /// </summary>
private ShellContext CreateSetupContext() { private ShellContext CreateSetupContext() {
Logger.Debug("Creating shell context for root setup"); Logger.Debug("Creating shell context for root setup.");
return _shellContextFactory.CreateSetupContext(new ShellSettings { Name = ShellSettings.DefaultName }); return _shellContextFactory.CreateSetupContext(new ShellSettings { Name = ShellSettings.DefaultName });
} }
/// <summary> /// <summary>
/// Creates a shell context based on shell settings /// Creates a shell context based on shell settings.
/// </summary> /// </summary>
private ShellContext CreateShellContext(ShellSettings settings) { private ShellContext CreateShellContext(ShellSettings settings) {
if (settings.State == TenantState.Uninitialized) { switch (settings.State) {
Logger.Debug("Creating shell context for tenant {0} setup", settings.Name); case TenantState.Uninitialized:
case TenantState.Initializing:
Logger.Debug("Creating shell context for tenant {0} setup.", settings.Name);
return _shellContextFactory.CreateSetupContext(settings); return _shellContextFactory.CreateSetupContext(settings);
} default:
Logger.Debug("Creating shell context for tenant {0}.", settings.Name);
Logger.Debug("Creating shell context for tenant {0}", settings.Name);
return _shellContextFactory.CreateShellContext(settings); return _shellContextFactory.CreateShellContext(settings);
} }
}
private void SetupExtensions() { private void SetupExtensions() {
_extensionLoaderCoordinator.SetupExtensions(); _extensionLoaderCoordinator.SetupExtensions();

View File

@@ -37,6 +37,7 @@ namespace Orchard.Environment {
ISweepGenerator sweepGenerator, ISweepGenerator sweepGenerator,
IEnumerable<IOwinMiddlewareProvider> owinMiddlewareProviders, IEnumerable<IOwinMiddlewareProvider> owinMiddlewareProviders,
ShellSettings shellSettings) { ShellSettings shellSettings) {
_eventsFactory = eventsFactory; _eventsFactory = eventsFactory;
_routeProviders = routeProviders; _routeProviders = routeProviders;
_httpRouteProviders = httpRouteProviders; _httpRouteProviders = httpRouteProviders;

View File

@@ -103,7 +103,8 @@ namespace Orchard.Environment.ShellBuilders {
Features = new[] { Features = new[] {
new ShellFeature { Name = "Orchard.Setup" }, new ShellFeature { Name = "Orchard.Setup" },
new ShellFeature { Name = "Shapes" }, new ShellFeature { Name = "Shapes" },
new ShellFeature { Name = "Orchard.jQuery" }, new ShellFeature { Name = "Orchard.Recipes" },
new ShellFeature { Name = "Orchard.jQuery" }
}, },
}; };