diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs index d449c33e1..133b5282c 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Commands/ImportExportCommands.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; +using System.Xml.Linq; using Orchard.Commands; +using Orchard.ContentManagement; using Orchard.ContentManagement.MetaData; -using Orchard.ImportExport.Recipes.Builders; +using Orchard.ImportExport.Models; using Orchard.ImportExport.Services; using Orchard.Recipes.Models; -using Orchard.Recipes.Providers.Builders; -using Orchard.Recipes.Services; using Orchard.Security; using Orchard.Settings; @@ -19,27 +18,26 @@ namespace Orchard.ImportExport.Commands { private readonly ISiteService _siteService; private readonly IMembershipService _membershipService; private readonly IAuthenticationService _authenticationService; - private readonly IOrchardServices _orchardServices; public ImportExportCommands( IImportExportService importExportService, IContentDefinitionManager contentDefinitionManager, ISiteService siteService, IMembershipService membershipService, - IAuthenticationService authenticationService, - IOrchardServices orchardServices) { + IAuthenticationService authenticationService) { _importExportService = importExportService; _contentDefinitionManager = contentDefinitionManager; _siteService = siteService; _membershipService = membershipService; _authenticationService = authenticationService; - _orchardServices = orchardServices; } [OrchardSwitch] public string Filename { get; set; } [OrchardSwitch] + public string ConfigFilename { get; set; } + [OrchardSwitch] public string Types { get; set; } [OrchardSwitch] public bool Metadata { get; set; } @@ -53,10 +51,9 @@ namespace Orchard.ImportExport.Commands { public bool SiteSettings { get; set; } [CommandName("import file")] - [CommandHelp("import file /Filename: \r\n\t" + "Imports the content of a file.")] - [OrchardSwitches("Filename")] + [CommandHelp("import file /Filename: [/ConfigFilename:]\r\n\t" + "Imports the content of a file.")] + [OrchardSwitches("Filename,ConfigFilename")] public void ImportFile() { - if (String.IsNullOrEmpty(Filename)) { Context.Output.WriteLine(T("Invalid file path")); return; @@ -67,66 +64,113 @@ namespace Orchard.ImportExport.Commands { return; } + // Impersonate the Site owner. + ImpersonateSuperUser(); + + // Read config file if specified. + var configurationDocument = ReadImportConfigurationFile(ConfigFilename); + + // Configure any steps based on the configuration. + _importExportService.ConfigureImportActions(new ConfigureImportActionsContext(configurationDocument)); + + // Import the file. _importExportService.Import(File.ReadAllText(Filename)); Context.Output.WriteLine(T("Import running...")); } [CommandName("export file")] - [CommandHelp("export file [/Types:, ... ,] [/Metadata:true|false] [/Data:true|false] [/Version:Published|Draft|Latest] [/SiteSettings:true|false] [/Steps:, ... ,]\r\n\t" + "Create an export file according to the specified options.")] - [OrchardSwitches("Types,Metadata,Data,Version,SiteSettings,Steps")] + [CommandHelp("export file [/Filename:] [/ConfigFilename:] [/Types:, ... ,] [/Metadata:true|false] [/Data:true|false] [/Version:Published|Draft|Latest] [/SiteSettings:true|false] [/Steps:, ... ,]\r\n\t" + "Create an export file according to the specified options.")] + [OrchardSwitches("Filename,ConfigFilename,Types,Metadata,Data,Version,SiteSettings,Steps")] public void ExportFile() { // Impersonate the Site owner. - var superUser = _siteService.GetSiteSettings().SuperUser; - var owner = _membershipService.GetUser(superUser); - _authenticationService.SetAuthenticatedUserForRequest(owner); + ImpersonateSuperUser(); - var versionOption = VersionHistoryOptions.Published; + // Read config file if specified. + var configurationDocument = UpdateExportConfiguration(ReadExportConfigurationFile(ConfigFilename), Types, Metadata, Data, Version, SiteSettings, Steps); - if (!String.IsNullOrEmpty(Version) && !Enum.TryParse(Version, out versionOption)) { - Context.Output.WriteLine(T("Invalid version option")); - return; - } - - var enteredTypes = (Types ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - var exportTypes = _contentDefinitionManager - .ListTypeDefinitions() - .Where(contentType => enteredTypes.Contains(contentType.Name)) - .Select(contentType => contentType.Name) - .ToList(); - - var enteredSteps = (Steps ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - var recipeBuilderSteps = new List(); - - if (Metadata || Data) { - var dataStep = _orchardServices.WorkContext.Resolve(); - - if(Data) - dataStep.DataContentTypes = exportTypes; - - if(Metadata) - dataStep.SchemaContentTypes = exportTypes; - - dataStep.VersionHistoryOptions = versionOption; - recipeBuilderSteps.Add(dataStep); - } - - if (SiteSettings) { - var siteSettingsStep = _orchardServices.WorkContext.Resolve(); - recipeBuilderSteps.Add(siteSettingsStep); - } - - if (enteredSteps.Any()) { - var customStepsStep = _orchardServices.WorkContext.Resolve(); - recipeBuilderSteps.Add(customStepsStep); - } + // Get all the steps based on the configuration. + var actions = _importExportService.ParseExportActions(configurationDocument); Context.Output.WriteLine(T("Export starting...")); + var exportContext = new ExportActionContext(); + _importExportService.Export(exportContext, actions); + var exportFilePath = _importExportService.WriteExportFile(exportContext.RecipeDocument); - var exportFilePath = _importExportService.Export(recipeBuilderSteps); + if (!String.IsNullOrEmpty(Filename)) { + var directory = Path.GetDirectoryName(Filename); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + File.Copy(exportFilePath, Filename, overwrite: true); + exportFilePath = Filename; + } Context.Output.WriteLine(T("Export completed at {0}", exportFilePath)); } + + private XDocument UpdateExportConfiguration(XDocument configurationDocument, string types, bool metadata, bool data, string version, bool siteSettings, string customSteps) { + var buildRecipeElement = GetOrCreateElement(configurationDocument.Root, "BuildRecipe"); + var stepsElement = GetOrCreateElement(buildRecipeElement, "Steps"); + + if (metadata || data) { + var contentStepElement = GetOrCreateElement(stepsElement, "Content"); + var enteredTypes = (types ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + var exportTypes = _contentDefinitionManager + .ListTypeDefinitions() + .Where(contentType => enteredTypes.Contains(contentType.Name)) + .Select(contentType => contentType.Name) + .ToList(); + + if (data) + contentStepElement.Attr("DataContentTypes", String.Join(",", exportTypes)); + + if (metadata) + contentStepElement.Attr("SchemaContentTypes", String.Join(",", exportTypes)); + + if (!String.IsNullOrEmpty(version)) { + VersionHistoryOptions versionHistoryOptions; + if (Enum.TryParse(version, true, out versionHistoryOptions)) { + contentStepElement.Attr("VersionHistoryOptions", versionHistoryOptions); + } + } + } + + if (siteSettings) { + GetOrCreateElement(stepsElement, "Settings"); + } + + if (!String.IsNullOrEmpty(customSteps)) { + var customStepsElement = GetOrCreateElement(stepsElement, "CustomSteps"); + customStepsElement.Attr("Steps", customSteps); + } + + return configurationDocument; + } + + private void ImpersonateSuperUser() { + var superUser = _siteService.GetSiteSettings().SuperUser; + var owner = _membershipService.GetUser(superUser); + _authenticationService.SetAuthenticatedUserForRequest(owner); + } + + private XDocument ReadExportConfigurationFile(string filePath) { + return !String.IsNullOrEmpty(filePath) && File.Exists(filePath) ? XDocument.Load(filePath) : new XDocument(new XElement("Export")); + } + + private XDocument ReadImportConfigurationFile(string filePath) { + return !String.IsNullOrEmpty(filePath) && File.Exists(filePath) ? XDocument.Load(filePath) : new XDocument(new XElement("Import")); + } + + private XElement GetOrCreateElement(XElement element, string childElementName) { + var childElement = element.Element(childElementName); + + if (childElement == null) { + childElement = new XElement(childElementName); + element.Add(childElement); + } + + return childElement; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Controllers/AdminController.cs index ab3c05b63..84540d017 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Controllers/AdminController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using Orchard.ContentManagement; @@ -64,12 +65,12 @@ namespace Orchard.ImportExport.Controllers { return View(viewModel); } - var context = new ImportActionContext { ActionResult = RedirectToAction("Import") }; - foreach(var action in actions) { - action.Execute(context); - } - - return context.ActionResult; + var context = new ImportActionContext(); + var executionId = _importExportService.Import(context, actions); + + return !String.IsNullOrEmpty(executionId) + ? RedirectToAction("ImportResult", new { executionId = context.ExecutionId }) + : RedirectToAction("Import"); } public ActionResult ImportResult(string executionId) { @@ -104,16 +105,16 @@ namespace Orchard.ImportExport.Controllers { foreach (var action in actions) { action.UpdateEditor(Services.New, this); } - - var exportActionContext = new ExportActionContext { - ActionResult = RedirectToAction("Export") - }; - - foreach (var action in actions) { - action.Execute(exportActionContext); - } - return exportActionContext.ActionResult; + var exportActionContext = new ExportActionContext(); + _importExportService.Export(exportActionContext, actions); + + var recipeDocument = exportActionContext.RecipeDocument; + var exportFilePath = _importExportService.WriteExportFile(recipeDocument); + var recipe = _recipeParser.ParseRecipe(recipeDocument); + var exportFileName = recipe.GetExportFileName(); + + return File(exportFilePath, "text/xml", exportFileName); } bool IUpdateModel.TryUpdateModel(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ConfigureImportActionsContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ConfigureImportActionsContext.cs new file mode 100644 index 000000000..17589aa8c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ConfigureImportActionsContext.cs @@ -0,0 +1,12 @@ +using System.Xml.Linq; + +namespace Orchard.ImportExport.Models { + public class ConfigureImportActionsContext { + + public ConfigureImportActionsContext(XDocument configurationDocument) { + ConfigurationDocument = configurationDocument; + } + + public XDocument ConfigurationDocument { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportActionContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportActionContext.cs index 7e4509824..94ec933a2 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportActionContext.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportActionContext.cs @@ -1,7 +1,10 @@ -using System.Web.Mvc; +using System.Xml.Linq; -namespace Orchard.ImportExport.Services { +namespace Orchard.ImportExport.Models { public class ExportActionContext { - public ActionResult ActionResult { get; set; } + public ExportActionContext() { + RecipeDocument = new XDocument(); + } + public XDocument RecipeDocument { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ExportContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportContext.cs similarity index 69% rename from src/Orchard.Web/Modules/Orchard.ImportExport/Services/ExportContext.cs rename to src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportContext.cs index f585e4b8f..6d702593b 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ExportContext.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ExportContext.cs @@ -1,7 +1,6 @@ using System.Xml.Linq; -using Orchard.ImportExport.Models; -namespace Orchard.ImportExport.Services { +namespace Orchard.ImportExport.Models { public class ExportContext { public XDocument Document { get; set; } public ExportOptions ExportOptions { get; set; } diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionConfigurationContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionConfigurationContext.cs index 707c315b9..b20ab7b88 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionConfigurationContext.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionConfigurationContext.cs @@ -3,7 +3,7 @@ using Orchard.Recipes.Models; namespace Orchard.ImportExport.Models { public class ImportActionConfigurationContext : ConfigurationContext { - protected ImportActionConfigurationContext(XElement configurationElement) : base(configurationElement) { + public ImportActionConfigurationContext(XElement configurationElement) : base(configurationElement) { } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionContext.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionContext.cs index 5e25a3fef..0cfa7bf7e 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionContext.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/ImportActionContext.cs @@ -1,7 +1,8 @@ -using System.Web.Mvc; +using System.Xml.Linq; namespace Orchard.ImportExport.Models { public class ImportActionContext { - public ActionResult ActionResult { get; set; } + public XDocument RecipeDocument { get; set; } + public string ExecutionId { get; set; } } } \ 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/Models/SetupContext.cs similarity index 93% rename from src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupContext.cs rename to src/Orchard.Web/Modules/Orchard.ImportExport/Models/SetupContext.cs index c1a68d146..3358ccff0 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupContext.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Models/SetupContext.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Xml.Linq; -namespace Orchard.ImportExport.Services { +namespace Orchard.ImportExport.Models { public class SetupContext { public string SiteName { get; set; } public string AdminUsername { get; set; } diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj b/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj index 8417a9909..11c73bea5 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Orchard.ImportExport.csproj @@ -78,6 +78,7 @@ + @@ -88,16 +89,16 @@ - + - + - + @@ -158,7 +159,7 @@ - + diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs index 507d646ad..137b82303 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ExportActions/BuildRecipeAction.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web.Mvc; using Orchard.ContentManagement; using Orchard.ImportExport.Models; using Orchard.ImportExport.Services; @@ -9,18 +8,15 @@ using Orchard.ImportExport.ViewModels; using Orchard.Mvc; using Orchard.Recipes.Models; using Orchard.Recipes.Services; -using Orchard.Utility.Extensions; namespace Orchard.ImportExport.Providers.ExportActions { public class BuildRecipeAction : ExportAction { private readonly IEnumerable _recipeBuilderSteps; - private readonly IImportExportService _importExportService; - private readonly IRecipeParser _recipeParser; + private readonly IRecipeBuilder _recipeBuilder; - public BuildRecipeAction(IEnumerable recipeBuilderSteps, IImportExportService importExportService, IRecipeParser recipeParser) { + public BuildRecipeAction(IEnumerable recipeBuilderSteps, IRecipeBuilder recipeBuilder) { _recipeBuilderSteps = recipeBuilderSteps; - _importExportService = importExportService; - _recipeParser = recipeParser; + _recipeBuilder = recipeBuilder; RecipeBuilderSteps = new List(); } @@ -67,44 +63,25 @@ namespace Orchard.ImportExport.Providers.ExportActions { } public override void Configure(ExportActionConfigurationContext context) { + RecipeBuilderSteps.Clear(); + var recipeBuilderStepsElement = context.ConfigurationElement.Element("Steps"); if (recipeBuilderStepsElement == null) return; - foreach (var step in _recipeBuilderSteps) { - var stepConfigurationElement = recipeBuilderStepsElement.Element(step.Name); + foreach (var stepElement in recipeBuilderStepsElement.Elements()) { + var step = _recipeBuilderSteps.SingleOrDefault(x => x.Name == stepElement.Name.LocalName); - if (stepConfigurationElement != null) { - var stepContext = new RecipeBuilderStepConfigurationContext(stepConfigurationElement); + if (step != null) { + var stepContext = new RecipeBuilderStepConfigurationContext(stepElement); step.Configure(stepContext); + RecipeBuilderSteps.Add(step); } } } public override void Execute(ExportActionContext context) { - var recipeDocument = _importExportService.Export(RecipeBuilderSteps); - var recipe = _recipeParser.ParseRecipe(recipeDocument); - var exportFileName = GetExportFileName(recipe); - var exportFilePath = _importExportService.WriteExportFile(recipeDocument); - var actionResult = new FilePathResult(exportFilePath, "text/xml"); - - actionResult.FileDownloadName = exportFileName; - context.ActionResult = actionResult; - } - - private string GetExportFileName(Recipe recipe) { - string format; - - if (String.IsNullOrWhiteSpace(recipe.Name) && String.IsNullOrWhiteSpace(recipe.Version)) - format = "export.xml"; - else if (String.IsNullOrWhiteSpace(recipe.Version)) - format = "{0}.recipe.xml"; - else if (String.IsNullOrWhiteSpace(recipe.Name)) - format = "export-{1}.recipe.xml"; - else - format = "{0}-{1}.recipe.xml"; - - return String.Format(format, recipe.Name.HtmlClassify(), recipe.Version); + context.RecipeDocument = _recipeBuilder.Build(RecipeBuilderSteps); } } } \ 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/ExecuteRecipeAction.cs similarity index 72% rename from src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs rename to src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/ExecuteRecipeAction.cs index 73e3e9a12..c884528ae 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/UploadRecipeAction.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Providers/ImportActions/ExecuteRecipeAction.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Web.Mvc; -using System.Web.Routing; using System.Xml.Linq; using Orchard.ContentManagement; using Orchard.Environment.Configuration; @@ -17,31 +15,34 @@ using Orchard.Recipes.Services; using Orchard.UI.Notify; namespace Orchard.ImportExport.Providers.ImportActions { - public class UploadRecipeAction : ImportAction { + public class ExecuteRecipeAction : ImportAction { private readonly IOrchardServices _orchardServices; - private readonly IImportExportService _importExportService; private readonly ISetupService _setupService; private readonly ShellSettings _shellSettings; private readonly IFeatureManager _featureManager; private readonly IEnumerable _recipeExecutionSteps; + private readonly IRecipeParser _recipeParser; + private readonly IRecipeExecutor _recipeExecutor; - public UploadRecipeAction( + public ExecuteRecipeAction( IOrchardServices orchardServices, - IImportExportService importExportService, ISetupService setupService, ShellSettings shellSettings, IFeatureManager featureManager, - IEnumerable recipeExecutionSteps) { + IEnumerable recipeExecutionSteps, + IRecipeParser recipeParser, + IRecipeExecutor recipeExecutor) { _orchardServices = orchardServices; - _importExportService = importExportService; _setupService = setupService; _shellSettings = shellSettings; _featureManager = featureManager; _recipeExecutionSteps = recipeExecutionSteps; + _recipeParser = recipeParser; + _recipeExecutor = recipeExecutor; } - public override string Name { get { return "UploadRecipe"; } } + public override string Name { get { return "ExecuteRecipe"; } } public XDocument RecipeDocument { get; set; } public bool ResetSite { get; set; } @@ -104,31 +105,11 @@ namespace Orchard.ImportExport.Providers.ImportActions { if (isValid) { // Read recipe file. RecipeDocument = XDocument.Parse(new StreamReader(file.InputStream).ReadToEnd()); - var orchardElement = RecipeDocument.Element("Orchard"); - - // Update execution steps. - var executionStepNames = viewModel.RecipeExecutionSteps.Select(x => x.Name); - var executionStepsQuery = - from name in executionStepNames - where orchardElement.Element(name) != null - let provider = _recipeExecutionSteps.SingleOrDefault(x => x.Name == name) - where provider != null - select provider; - var executionSteps = executionStepsQuery.ToArray(); - foreach (var executionStep in executionSteps) { - var context = new UpdateRecipeExecutionStepContext { - RecipeDocument = RecipeDocument, - Step = orchardElement.Element(executionStep.Name) - }; - - // Give the execution step a chance to augment the recipe step before it will be scheduled. - executionStep.UpdateStep(context); - } } } } - return shapeFactory.EditorTemplate(TemplateName: "ImportActions/UploadRecipe", Model: viewModel, Prefix: Prefix); + return shapeFactory.EditorTemplate(TemplateName: "ImportActions/ExecuteRecipe", Model: viewModel, Prefix: Prefix); } public override void Configure(ImportActionConfigurationContext context) { @@ -139,37 +120,42 @@ namespace Orchard.ImportExport.Providers.ImportActions { if (executionStepsElement == null) return; - foreach (var step in _recipeExecutionSteps) { - var stepConfigurationElement = executionStepsElement.Element(step.Name); + foreach (var stepElement in executionStepsElement.Elements()) { + var step = _recipeExecutionSteps.SingleOrDefault(x => x.Name == stepElement.Name.LocalName); - if (stepConfigurationElement != null) { - var stepContext = new RecipeExecutionStepConfigurationContext(stepConfigurationElement); + if (step != null) { + var stepContext = new RecipeExecutionStepConfigurationContext(stepElement); step.Configure(stepContext); } } } public override void Execute(ImportActionContext context) { - if (RecipeDocument == null) + var recipeDocument = context.RecipeDocument ?? RecipeDocument; + if (recipeDocument == null) return; + // Give each execution step a chance to augment the recipe step before it will be scheduled. + PrepareRecipe(recipeDocument); + // Sets the request timeout to 10 minutes to give enough time to execute custom recipes. _orchardServices.WorkContext.HttpContext.Server.ScriptTimeout = 600; - var executionId = ResetSite ? Setup() : ExecuteRecipe(); + var executionId = ResetSite ? Setup(recipeDocument) : ExecuteRecipe(recipeDocument); if(executionId == null) { _orchardServices.Notifier.Warning(T("The recipe contained no steps. No work was scheduled.")); return; } - context.ActionResult = new RedirectToRouteResult(new RouteValueDictionary(new { action = "ImportResult", controller = "Admin", area = "Orchard.ImportExport", executionId = executionId })); + context.ExecutionId = executionId; + context.RecipeDocument = recipeDocument; } - private string Setup() { + private string Setup(XDocument recipeDocument) { var setupContext = new SetupContext { DropExistingTables = true, - RecipeDocument = RecipeDocument, + RecipeDocument = recipeDocument, AdminPassword = SuperUserPassword, AdminUsername = _orchardServices.WorkContext.CurrentSite.SuperUser, DatabaseConnectionString = _shellSettings.DataConnectionString, @@ -181,8 +167,22 @@ namespace Orchard.ImportExport.Providers.ImportActions { return _setupService.Setup(setupContext); } - private string ExecuteRecipe() { - return _importExportService.Import(RecipeDocument); + private string ExecuteRecipe(XDocument recipeDocument) { + var recipe = _recipeParser.ParseRecipe(recipeDocument); + return _recipeExecutor.Execute(recipe); + } + + private void PrepareRecipe(XDocument recipeDocument) { + var query = + from stepElement in recipeDocument.Element("Orchard").Elements() + let step = _recipeExecutionSteps.SingleOrDefault(x => x.Name == stepElement.Name.LocalName) + where step != null + select new { Step = step, StepElement = stepElement }; + + foreach (var step in query) { + var context = new UpdateRecipeExecutionStepContext { Step = step.StepElement }; + step.Step.UpdateStep(context); + } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IExportEventHandler.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IExportEventHandler.cs index c9d912d2c..65c683064 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IExportEventHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IExportEventHandler.cs @@ -1,5 +1,6 @@ using System; using Orchard.Events; +using Orchard.ImportExport.Models; namespace Orchard.ImportExport.Services { [Obsolete("Implement IRecipeExecutionStep instead.")] diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IImportExportService.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IImportExportService.cs index d282e6953..d7e8ffebc 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IImportExportService.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/IImportExportService.cs @@ -1,18 +1,42 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; -using Orchard.Recipes.Services; +using Orchard.ImportExport.Models; +using Orchard.Recipes.Models; +using Orchard.Utility.Extensions; namespace Orchard.ImportExport.Services { public interface IImportExportService : IDependency { - string Import(XDocument recipeDocument); - XDocument Export(IEnumerable steps); + string Import(ImportActionContext context, IEnumerable actions = null); + void Export(ExportActionContext context, IEnumerable actions = null); string WriteExportFile(XDocument recipeDocument); + IEnumerable ParseExportActions(XDocument configurationDocument); + void ConfigureImportActions(ConfigureImportActionsContext context); } public static class ImportExportServiceExtensions { + public static string Import(this IImportExportService service, string recipeText) { - var recipeDocument = XDocument.Parse(recipeText, LoadOptions.PreserveWhitespace); - return service.Import(recipeDocument); + var context = new ImportActionContext { + RecipeDocument = XDocument.Parse(recipeText, LoadOptions.PreserveWhitespace) + }; + service.Import(context); + return context.ExecutionId; + } + + public static string GetExportFileName(this Recipe recipe) { + string format; + + if (String.IsNullOrWhiteSpace(recipe.Name) && String.IsNullOrWhiteSpace(recipe.Version)) + format = "export.xml"; + else if (String.IsNullOrWhiteSpace(recipe.Version)) + format = "{0}.recipe.xml"; + else if (String.IsNullOrWhiteSpace(recipe.Name)) + format = "export-{1}.recipe.xml"; + else + format = "{0}-{1}.recipe.xml"; + + return String.Format(format, recipe.Name.HtmlClassify(), recipe.Version); } } } \ 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 index ce57a9829..176bf128c 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ISetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ISetupService.cs @@ -1,4 +1,6 @@ -namespace Orchard.ImportExport.Services { +using Orchard.ImportExport.Models; + +namespace Orchard.ImportExport.Services { public interface ISetupService : IDependency { string Setup(SetupContext context); } diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ImportExportService.cs b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ImportExportService.cs index 7ae2ef7be..153eb1135 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ImportExportService.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/ImportExportService.cs @@ -1,44 +1,46 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Linq; using Orchard.FileSystems.AppData; -using Orchard.Recipes.Services; +using Orchard.ImportExport.Models; +using Orchard.Logging; using Orchard.Services; namespace Orchard.ImportExport.Services { public class ImportExportService : Component, IImportExportService { private readonly IOrchardServices _orchardServices; private readonly IAppDataFolder _appDataFolder; - private readonly IRecipeBuilder _recipeBuilder; - private readonly IRecipeParser _recipeParser; - private readonly IRecipeExecutor _recipeExecutor; private readonly IClock _clock; + private readonly IEnumerable _exportActions; + private readonly IEnumerable _importActions; private const string ExportsDirectory = "Exports"; public ImportExportService( IOrchardServices orchardServices, IAppDataFolder appDataFolder, - IRecipeBuilder recipeBuilder, - IRecipeParser recipeParser, - IRecipeExecutor recipeExecutor, - IClock clock) { + IClock clock, + IEnumerable exportActions, + IEnumerable importActions) { _orchardServices = orchardServices; _appDataFolder = appDataFolder; - _recipeBuilder = recipeBuilder; - _recipeParser = recipeParser; - _recipeExecutor = recipeExecutor; _clock = clock; + _exportActions = exportActions; + _importActions = importActions; } - public string Import(XDocument recipeDocument) { - var recipe = _recipeParser.ParseRecipe(recipeDocument); - return _recipeExecutor.Execute(recipe); + public string Import(ImportActionContext context, IEnumerable actions = null) { + foreach (var action in actions ?? _importActions) { + action.Execute(context); + } + return context.ExecutionId; } - public XDocument Export(IEnumerable steps) { - var recipe = _recipeBuilder.Build(steps); - return recipe; + public void Export(ExportActionContext context, IEnumerable actions = null) { + foreach (var action in actions ?? _exportActions) { + action.Execute(context); + } } public string WriteExportFile(XDocument recipeDocument) { @@ -53,5 +55,49 @@ namespace Orchard.ImportExport.Services { return _appDataFolder.MapPath(path); } + + public IEnumerable ParseExportActions(XDocument configurationDocument) { + var actionElements = configurationDocument.Root.Elements(); + + foreach (var actionElement in actionElements) { + var action = _exportActions.SingleOrDefault(x => x.Name == actionElement.Name.LocalName); + + if (action == null) { + Logger.Warning("The export action '{0}' could not be found. Did you forget to enable a feature?", actionElement.Name.LocalName); + continue; + } + + action.Configure(new ExportActionConfigurationContext(actionElement)); + yield return action; + } + } + + public void ConfigureImportActions(ConfigureImportActionsContext context) { + var actionConfigElements = context.ConfigurationDocument.Root; + + foreach (var action in _importActions) { + var actionConfigElement = actionConfigElements.Element(action.Name); + + if (actionConfigElement != null) { + action.Configure(new ImportActionConfigurationContext(actionConfigElement)); + } + } + } + + public IEnumerable ParseImportActions(XDocument configurationDocument) { + var actionElements = configurationDocument.Root.Elements(); + + foreach (var actionElement in actionElements) { + var action = _importActions.SingleOrDefault(x => x.Name == actionElement.Name.LocalName); + + if (action == null) { + Logger.Warning("The import action '{0}' could not be found. Did you forget to enable a feature?", actionElement.Name.LocalName); + continue; + } + + action.Configure(new ImportActionConfigurationContext(actionElement)); + yield return action; + } + } } } \ 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 index 2e99cf5ef..34d1a5390 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Services/SetupService.cs @@ -14,6 +14,7 @@ using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.ShellBuilders; using Orchard.Environment.State; +using Orchard.ImportExport.Models; using Orchard.Localization.Services; using Orchard.Logging; using Orchard.Recipes.Services; diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml similarity index 100% rename from src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/UploadRecipe.cshtml rename to src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Builders/ContentStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Builders/ContentStep.cs index 3e4325952..b0be17f21 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Builders/ContentStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Builders/ContentStep.cs @@ -72,16 +72,15 @@ namespace Orchard.Recipes.Providers.Builders { public override void Configure(RecipeBuilderStepConfigurationContext context) { var schemaContentTypeNames = context.ConfigurationElement.Attr("SchemaContentTypes"); var dataContentTypeNames = context.ConfigurationElement.Attr("DataContentTypes"); - var versionHistoryOptions = context.ConfigurationElement.Attr("VersionHistoryOptions"); + var versionHistoryOptions = context.ConfigurationElement.Attr("VersionHistoryOptions"); - if (!string.IsNullOrWhiteSpace(schemaContentTypeNames)) + if (!String.IsNullOrWhiteSpace(schemaContentTypeNames)) SchemaContentTypes = schemaContentTypeNames.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList(); - if (!string.IsNullOrWhiteSpace(dataContentTypeNames)) + if (!String.IsNullOrWhiteSpace(dataContentTypeNames)) DataContentTypes = dataContentTypeNames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); - if(versionHistoryOptions != null) - VersionHistoryOptions = versionHistoryOptions.Value; + VersionHistoryOptions = versionHistoryOptions; } public override void Build(BuildContext context) { diff --git a/src/Orchard/Mvc/MvcModule.cs b/src/Orchard/Mvc/MvcModule.cs index f2ac24f56..08d46530e 100644 --- a/src/Orchard/Mvc/MvcModule.cs +++ b/src/Orchard/Mvc/MvcModule.cs @@ -128,6 +128,10 @@ namespace Orchard.Mvc { get { return HttpRuntime.Cache; } } + public override HttpServerUtilityBase Server { + get { return new HttpServerUtilityPlaceholder(); } + } + public override object GetService(Type serviceType) { return null; } @@ -265,5 +269,9 @@ namespace Orchard.Mvc { public override bool Cookies { get { return true; } } public override ArrayList Browsers { get { return new ArrayList(); } } } + + public class HttpServerUtilityPlaceholder : HttpServerUtilityBase { + public override int ScriptTimeout { get; set; } + } } } \ No newline at end of file