diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Module.txt b/src/Orchard.Web/Modules/Orchard.ImportExport/Module.txt
index 283ec6619..3b6960348 100644
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Module.txt
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Module.txt
@@ -11,9 +11,4 @@ Features:
Name: Import Export
Description: Imports and exports content item data.
Category: Content
- Dependencies: Orchard.jQuery, Orchard.Recipes
- Orchard.ImportExport.ResetSite
- Name: Reset Site Action
- Description: Adds a Reset Site action to the Import screen.
- Category: Hosting
- Dependencies: Orchard.ImportExport, Orchard.MultiTenancy
\ No newline at end of file
+ Dependencies: Orchard.jQuery, Orchard.Recipes
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj b/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj
index 029ea8a1d..aee8aa047 100644
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj
@@ -48,7 +48,15 @@
false
+
+ False
+ ..\..\..\..\lib\autofac\Autofac.dll
+
+
+ False
+ ..\..\..\..\lib\nhibernate\NHibernate.dll
+
@@ -72,8 +80,10 @@
-
-
+
+
+
+
@@ -149,9 +159,6 @@
-
-
- 10.0$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/ResetSiteAction.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/ResetSiteAction.cs
deleted file mode 100644
index c8c2b316f..000000000
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/ResetSiteAction.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.Web.Mvc;
-using Orchard.ContentManagement;
-using Orchard.Environment.Configuration;
-using Orchard.Environment.Extensions;
-using Orchard.ImportExport.Services;
-using Orchard.ImportExport.ViewModels;
-using Orchard.MultiTenancy.Services;
-
-namespace Orchard.ImportExport.Providers.ImportActions {
- [OrchardFeature("Orchard.ImportExport.ResetSite")]
- public class ResetSiteAction : ImportAction {
- private readonly ITenantService _tenantService;
- private readonly ShellSettings _shellSettings;
-
- public ResetSiteAction(ITenantService tenantService, ShellSettings shellSettings) {
- _tenantService = tenantService;
- _shellSettings = shellSettings;
- }
-
- public override string Name { get { return "ResetSite"; } }
-
- public override int Priority {
- get { return 500; }
- }
-
- public override dynamic BuildEditor(dynamic shapeFactory) {
- return UpdateEditor(shapeFactory, null);
- }
-
- public bool ResetSite { get; set; }
- public bool DropTables { get; set; }
-
- public override dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater) {
- var viewModel = new ResetSiteViewModel();
- if (updater != null) {
- ResetSite = viewModel.ResetSite;
- DropTables = viewModel.DropTables;
- }
-
- return shapeFactory.EditorTemplate(TemplateName: "ImportActions/ResetSite", Model: viewModel, Prefix: Prefix);
- }
-
- public override void Execute(ImportActionContext context) {
- _shellSettings.State = TenantState.Disabled;
- _tenantService.ResetTenant(_shellSettings, DropTables);
- _shellSettings.DataProvider = null;
- _tenantService.UpdateTenant(_shellSettings);
- context.ActionResult = new RedirectResult("~/");
- }
- }
-}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs
index fc39dc483..6d9749607 100644
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs
@@ -1,44 +1,93 @@
using System.IO;
+using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.ContentManagement;
+using Orchard.Environment.Configuration;
+using Orchard.Environment.Features;
using Orchard.ImportExport.Services;
+using Orchard.ImportExport.ViewModels;
namespace Orchard.ImportExport.Providers.ImportActions {
public class UploadRecipeAction : ImportAction {
private readonly IOrchardServices _orchardServices;
private readonly IImportExportService _importExportService;
+ private readonly ISetupService _setupService;
+ private readonly ShellSettings _shellSettings;
+ private readonly IFeatureManager _featureManager;
+
+ public UploadRecipeAction(
+ IOrchardServices orchardServices,
+ IImportExportService importExportService,
+ ISetupService setupService,
+ ShellSettings shellSettings,
+ IFeatureManager featureManager) {
- public UploadRecipeAction(IOrchardServices orchardServices, IImportExportService importExportService) {
_orchardServices = orchardServices;
_importExportService = importExportService;
+ _setupService = setupService;
+ _shellSettings = shellSettings;
+ _featureManager = featureManager;
}
public override string Name { get { return "UploadRecipe"; } }
public HttpPostedFileBase File { get; set; }
+ public bool ResetSite { get; set; }
public override dynamic BuildEditor(dynamic shapeFactory) {
return UpdateEditor(shapeFactory, null);
}
public override dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater) {
-
+ var viewModel = new UploadRecipeViewModel();
+
if (updater != null) {
- var request = _orchardServices.WorkContext.HttpContext.Request;
- File = request.Files["RecipeFile"];
+
+ if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
+ var request = _orchardServices.WorkContext.HttpContext.Request;
+
+ File = request.Files["RecipeFile"];
+ ResetSite = viewModel.ResetSite;
+
+ if (File == null)
+ updater.AddModelError("RecipeFile", T("No recipe file selected."));
+ }
}
- return shapeFactory.EditorTemplate(TemplateName: "ImportActions/UploadRecipe", Prefix: Prefix);
+ return shapeFactory.EditorTemplate(TemplateName: "ImportActions/UploadRecipe", Model: viewModel, Prefix: Prefix);
}
public override void Execute(ImportActionContext context) {
if (File == null || File.ContentLength == 0)
return;
- var executionId = _importExportService.Import(new StreamReader(File.InputStream).ReadToEnd());
+ var executionId = ResetSite ? Setup() : ExecuteRecipe();
context.ActionResult = new RedirectToRouteResult(new RouteValueDictionary(new { action = "ImportResult", controller = "Admin", area = "Orchard.ImportExport", executionId = executionId }));
}
+
+ private string Setup() {
+ var setupContext = new SetupContext {
+ DropExistingTables = true,
+ RecipeText = ReadRecipeFile(),
+ AdminPassword = "password",
+ AdminUsername = _orchardServices.WorkContext.CurrentSite.SuperUser,
+ DatabaseConnectionString = _shellSettings.DataConnectionString,
+ DatabaseProvider = _shellSettings.DataProvider,
+ DatabaseTablePrefix = _shellSettings.DataTablePrefix,
+ SiteName = _orchardServices.WorkContext.CurrentSite.SiteName,
+ EnabledFeatures = _featureManager.GetEnabledFeatures().Select(x => x.Id).ToArray()
+ };
+ return _setupService.Setup(setupContext);
+ }
+
+ private string ExecuteRecipe() {
+ return _importExportService.Import(ReadRecipeFile());
+ }
+
+ private string ReadRecipeFile() {
+ return new StreamReader(File.InputStream).ReadToEnd();
+ }
}
}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ISetupService.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ISetupService.cs
new file mode 100644
index 000000000..ce57a9829
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ISetupService.cs
@@ -0,0 +1,5 @@
+namespace Orchard.ImportExport.Services {
+ public interface ISetupService : IDependency {
+ string Setup(SetupContext context);
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupContext.cs
new file mode 100644
index 000000000..bb29f6aea
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupContext.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace Orchard.ImportExport.Services {
+ public class SetupContext {
+ public string SiteName { get; set; }
+ public string AdminUsername { get; set; }
+ public string AdminPassword { get; set; }
+ public string DatabaseProvider { get; set; }
+ public string DatabaseConnectionString { get; set; }
+ public string DatabaseTablePrefix { get; set; }
+ public IEnumerable EnabledFeatures { get; set; }
+ public string RecipeText { get; set; }
+ public bool DropExistingTables { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs
new file mode 100644
index 000000000..a64403185
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Web;
+using Orchard.ContentManagement;
+using Orchard.Core.Settings.Models;
+using Orchard.Data;
+using Orchard.Data.Migration;
+using Orchard.Data.Migration.Interpreters;
+using Orchard.Data.Migration.Schema;
+using Orchard.Environment;
+using Orchard.Environment.Configuration;
+using Orchard.Environment.Descriptor;
+using Orchard.Environment.Descriptor.Models;
+using Orchard.Environment.ShellBuilders;
+using Orchard.Environment.State;
+using Orchard.Localization.Services;
+using Orchard.Logging;
+using Orchard.Recipes.Services;
+using Orchard.Security;
+using Orchard.Settings;
+using Orchard.Utility.Extensions;
+
+namespace Orchard.ImportExport.Services
+{
+ public class SetupService : Component, ISetupService {
+ private readonly ShellSettings _shellSettings;
+ private readonly IOrchardHost _orchardHost;
+ private readonly IShellSettingsManager _shellSettingsManager;
+ private readonly IShellContainerFactory _shellContainerFactory;
+ private readonly ICompositionStrategy _compositionStrategy;
+ private readonly IProcessingEngine _processingEngine;
+
+ public SetupService(
+ ShellSettings shellSettings,
+ IOrchardHost orchardHost,
+ IShellSettingsManager shellSettingsManager,
+ IShellContainerFactory shellContainerFactory,
+ ICompositionStrategy compositionStrategy,
+ IProcessingEngine processingEngine) {
+
+ _shellSettings = shellSettings;
+ _orchardHost = orchardHost;
+ _shellSettingsManager = shellSettingsManager;
+ _shellContainerFactory = shellContainerFactory;
+ _compositionStrategy = compositionStrategy;
+ _processingEngine = processingEngine;
+ }
+
+ public string Setup(SetupContext context) {
+ string executionId;
+
+ Logger.Information("Running setup for tenant '{0}'.", _shellSettings.Name);
+
+ // The vanilla Orchard distibution has the following features enabled.
+ string[] hardcoded = {
+ // Framework
+ "Orchard.Framework",
+ // Core
+ "Common", "Containers", "Contents", "Dashboard", "Feeds", "Navigation","Scheduling", "Settings", "Shapes", "Title",
+ // Modules
+ "Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules",
+ "PackagingServices", "Orchard.Packaging", "Gallery", "Orchard.Recipes"
+ };
+
+ context.EnabledFeatures = hardcoded.Union(context.EnabledFeatures ?? Enumerable.Empty()).Distinct().ToList();
+
+ var shellSettings = new ShellSettings(_shellSettings);
+
+ if (String.IsNullOrEmpty(shellSettings.DataProvider)) {
+ shellSettings.DataProvider = context.DatabaseProvider;
+ shellSettings.DataConnectionString = context.DatabaseConnectionString;
+ shellSettings.DataTablePrefix = context.DatabaseTablePrefix;
+ }
+
+ shellSettings.EncryptionAlgorithm = "AES";
+ shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString();
+ shellSettings.HashAlgorithm = "HMACSHA256";
+ shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString();
+
+ var shellDescriptor = new ShellDescriptor {
+ Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
+ };
+
+ var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
+
+ // Initialize database explicitly, and store shell descriptor.
+ using (var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellBlueprint)) {
+
+ using (var environment = bootstrapLifetimeScope.CreateWorkContextScope()) {
+
+ // Check if the database is already created (in case an exception occured in the second phase).
+ var schemaBuilder = new SchemaBuilder(environment.Resolve());
+ var installationPresent = true;
+ try {
+ var tablePrefix = String.IsNullOrEmpty(shellSettings.DataTablePrefix) ? "" : shellSettings.DataTablePrefix + "_";
+ schemaBuilder.ExecuteSql("SELECT * FROM " + tablePrefix + "Settings_ShellDescriptorRecord");
+ }
+ catch {
+ installationPresent = false;
+ }
+
+ if (installationPresent) {
+ if (context.DropExistingTables) {
+ DropTenantDatabaseTables(environment);
+ }
+ }
+
+ // Make a workaround to avoid the Transaction issue for PostgreSQL.
+ environment.Resolve().RequireNew();
+
+ schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table
+ .Column("Id", column => column.PrimaryKey().Identity())
+ .Column("DataMigrationClass")
+ .Column("Version"));
+
+ var dataMigrationManager = environment.Resolve();
+ dataMigrationManager.Update("Settings");
+
+ foreach (var feature in context.EnabledFeatures) {
+ dataMigrationManager.Update(feature);
+ }
+
+ var descriptorManager = environment.Resolve();
+ descriptorManager.UpdateShellDescriptor(0, shellDescriptor.Features, shellDescriptor.Parameters);
+ }
+ }
+
+ // In effect "pump messages" see PostMessage circa 1980.
+ while ( _processingEngine.AreTasksPending() )
+ _processingEngine.ExecuteNextTask();
+
+ // Creating a standalone environment.
+ // 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.
+
+ // Must mark state as Running - otherwise standalone environment is created "for setup".
+ shellSettings.State = TenantState.Running;
+ using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) {
+ try {
+ executionId = CreateTenantData(context, environment);
+ }
+ catch {
+ environment.Resolve().Cancel();
+ throw;
+ }
+ }
+
+ _shellSettingsManager.SaveSettings(shellSettings);
+
+ return executionId;
+ }
+
+ private string CreateTenantData(SetupContext context, IWorkContextScope environment) {
+ // Create superuser.
+ var membershipService = environment.Resolve();
+ var user = membershipService.CreateUser(
+ new CreateUserParams(
+ context.AdminUsername,
+ context.AdminPassword,
+ email: String.Empty,
+ passwordQuestion: String.Empty,
+ passwordAnswer: String.Empty,
+ isApproved: true));
+
+ // Set superuser as current user for request (it will be set as the owner of all content items).
+ var authenticationService = environment.Resolve();
+ authenticationService.SetAuthenticatedUserForRequest(user);
+
+ // Set site name and settings.
+ var siteService = environment.Resolve();
+ var siteSettings = siteService.GetSiteSettings().As();
+ siteSettings.SiteSalt = Guid.NewGuid().ToString("N");
+ siteSettings.SiteName = context.SiteName;
+ siteSettings.SuperUser = context.AdminUsername;
+ siteSettings.SiteCulture = "en-US";
+
+ // Add default culture.
+ var cultureManager = environment.Resolve();
+ cultureManager.AddCulture("en-US");
+
+ // Execute recipe
+ var recipeParser = environment.Resolve();
+ var recipe = recipeParser.ParseRecipe(context.RecipeText);
+ var recipeManager = environment.Resolve();
+ var executionId = recipeManager.Execute(recipe);
+
+ // Null check: temporary fix for running setup in command line.
+ if (HttpContext.Current != null) {
+ authenticationService.SignIn(user, true);
+ }
+
+ return executionId;
+ }
+
+ private void DropTenantDatabaseTables(IWorkContextScope environment) {
+ var sessionFactoryHolder = environment.Resolve();
+ var schemaBuilder = new SchemaBuilder(environment.Resolve());
+ var configuration = sessionFactoryHolder.GetConfiguration();
+ foreach (var mapping in configuration.ClassMappings) {
+ try {
+ schemaBuilder.DropTable(mapping.Table.Name);
+ }
+ catch (Exception ex) {
+ Logger.Warning(ex, "Failed to drop table '{0}'.", mapping.Table.Name);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/ResetSiteViewModel.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/UploadRecipeViewModel.cs
similarity index 54%
rename from src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/ResetSiteViewModel.cs
rename to src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/UploadRecipeViewModel.cs
index 498d8e01f..f5046b4fc 100644
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/ResetSiteViewModel.cs
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/ViewModels/UploadRecipeViewModel.cs
@@ -1,6 +1,5 @@
namespace Orchard.ImportExport.ViewModels {
- public class ResetSiteViewModel {
+ public class UploadRecipeViewModel {
public bool ResetSite { get; set; }
- public bool DropTables { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ResetSite.cshtml b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ResetSite.cshtml
deleted file mode 100644
index 78a891b43..000000000
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ResetSite.cshtml
+++ /dev/null
@@ -1,16 +0,0 @@
-@model Orchard.ImportExport.ViewModels.ResetSiteViewModel
-@{
- Script.Require("ShapesBase");
-}
-
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml
index 052ca97ca..37d6e4ea5 100644
--- a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml
+++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml
@@ -1,8 +1,18 @@
-@model dynamic
+@model Orchard.ImportExport.ViewModels.UploadRecipeViewModel
@T("Choose a recipe file to import. Please consider {0} or backing up your data first.", @Html.Link(T("exporting").Text, Url.Action("Export", "Admin", new { area = "Orchard.ImportExport" })))