mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Enhanced RecipeExecutionSteps to provide and handle UI on the import screen.
This commit is contained in:
@@ -69,6 +69,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.WebPages.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
@@ -80,6 +84,7 @@
|
||||
<Compile Include="Permissions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Providers\ExportActions\BuildRecipeAction.cs" />
|
||||
<Compile Include="ViewModels\RecipeExecutionStepViewModel.cs" />
|
||||
<Compile Include="Services\ISetupService.cs" />
|
||||
<Compile Include="Services\SetupContext.cs" />
|
||||
<Compile Include="Services\SetupService.cs" />
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Features;
|
||||
using Orchard.ImportExport.Services;
|
||||
using Orchard.ImportExport.ViewModels;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
public class UploadRecipeAction : ImportAction {
|
||||
@@ -17,24 +21,27 @@ namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
private readonly ISetupService _setupService;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
private readonly IFeatureManager _featureManager;
|
||||
private readonly IEnumerable<IRecipeExecutionStep> _recipeExecutionSteps;
|
||||
|
||||
public UploadRecipeAction(
|
||||
IOrchardServices orchardServices,
|
||||
IImportExportService importExportService,
|
||||
ISetupService setupService,
|
||||
ShellSettings shellSettings,
|
||||
IFeatureManager featureManager) {
|
||||
IFeatureManager featureManager,
|
||||
IEnumerable<IRecipeExecutionStep> recipeExecutionSteps) {
|
||||
|
||||
_orchardServices = orchardServices;
|
||||
_importExportService = importExportService;
|
||||
_setupService = setupService;
|
||||
_shellSettings = shellSettings;
|
||||
_featureManager = featureManager;
|
||||
_recipeExecutionSteps = recipeExecutionSteps;
|
||||
}
|
||||
|
||||
public override string Name { get { return "UploadRecipe"; } }
|
||||
|
||||
public HttpPostedFileBase File { get; set; }
|
||||
public XDocument RecipeDocument { get; set; }
|
||||
public bool ResetSite { get; set; }
|
||||
public string SuperUserPassword { get; set; }
|
||||
|
||||
@@ -44,26 +51,63 @@ namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
|
||||
public override dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater) {
|
||||
var viewModel = new UploadRecipeViewModel {
|
||||
SuperUserName = _orchardServices.WorkContext.CurrentSite.SuperUser
|
||||
SuperUserName = _orchardServices.WorkContext.CurrentSite.SuperUser,
|
||||
RecipeExecutionSteps = _recipeExecutionSteps.Select(x => new RecipeExecutionStepViewModel {
|
||||
Name = x.Name,
|
||||
DisplayName = x.DisplayName,
|
||||
Description = x.Description,
|
||||
Editor = x.BuildEditor(shapeFactory)
|
||||
}).Where(x => x.Editor != null).ToList()
|
||||
};
|
||||
|
||||
if (updater != null) {
|
||||
|
||||
if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
|
||||
// Validate and read uploaded recipe file.
|
||||
var request = _orchardServices.WorkContext.HttpContext.Request;
|
||||
var file = request.Files["RecipeFile"];
|
||||
var isInValid = false;
|
||||
|
||||
File = request.Files["RecipeFile"];
|
||||
ResetSite = viewModel.ResetSite;
|
||||
SuperUserPassword = viewModel.SuperUserPassword;
|
||||
|
||||
if (File == null || File.ContentLength == 0)
|
||||
if (file == null || file.ContentLength == 0) {
|
||||
updater.AddModelError("RecipeFile", T("No recipe file selected."));
|
||||
isInValid = true;
|
||||
}
|
||||
|
||||
if (ResetSite) {
|
||||
if(String.IsNullOrWhiteSpace(viewModel.SuperUserPassword))
|
||||
if (String.IsNullOrWhiteSpace(viewModel.SuperUserPassword)) {
|
||||
updater.AddModelError("SuperUserPassword", T("Please specify a new password for the super user."));
|
||||
else if(!String.Equals(viewModel.SuperUserPassword, viewModel.SuperUserPasswordConfirmation))
|
||||
isInValid = true;
|
||||
}
|
||||
else if (!String.Equals(viewModel.SuperUserPassword, viewModel.SuperUserPasswordConfirmation)) {
|
||||
updater.AddModelError("SuperUserPassword", T("The passwords do not match."));
|
||||
isInValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInValid) {
|
||||
// Read recipe file.
|
||||
RecipeDocument = XDocument.Parse(new StreamReader(file.InputStream).ReadToEnd());
|
||||
var orchardElement = RecipeDocument.Element("Orchard");
|
||||
|
||||
// Update execution steps.
|
||||
var executionStepNames = viewModel.RecipeExecutionSteps.Where(x => x.IsSelected).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();
|
||||
var stepUpdater = new Updater(updater, secondHalf => String.Format("{0}.{1}", Prefix, secondHalf));
|
||||
foreach (var executionStep in executionSteps) {
|
||||
var context = new UpdateRecipeExecutionStepContext {
|
||||
RecipeDocument = RecipeDocument,
|
||||
Step = orchardElement.Element(executionStep.Name)
|
||||
};
|
||||
executionStep.UpdateEditor(shapeFactory, stepUpdater, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +116,7 @@ namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
}
|
||||
|
||||
public override void Execute(ImportActionContext context) {
|
||||
if (File == null || File.ContentLength == 0)
|
||||
if (RecipeDocument == null)
|
||||
return;
|
||||
|
||||
var executionId = ResetSite ? Setup() : ExecuteRecipe();
|
||||
@@ -82,7 +126,7 @@ namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
private string Setup() {
|
||||
var setupContext = new SetupContext {
|
||||
DropExistingTables = true,
|
||||
RecipeText = ReadRecipeFile(),
|
||||
RecipeText = RecipeDocument.ToString(SaveOptions.DisableFormatting),
|
||||
AdminPassword = SuperUserPassword,
|
||||
AdminUsername = _orchardServices.WorkContext.CurrentSite.SuperUser,
|
||||
DatabaseConnectionString = _shellSettings.DataConnectionString,
|
||||
@@ -95,11 +139,7 @@ namespace Orchard.ImportExport.Providers.ImportActions {
|
||||
}
|
||||
|
||||
private string ExecuteRecipe() {
|
||||
return _importExportService.Import(ReadRecipeFile());
|
||||
}
|
||||
|
||||
private string ReadRecipeFile() {
|
||||
return new StreamReader(File.InputStream).ReadToEnd();
|
||||
return _importExportService.Import(RecipeDocument.ToString(SaveOptions.DisableFormatting));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.ImportExport.ViewModels {
|
||||
public class RecipeExecutionStepViewModel {
|
||||
public string Name { get; set; }
|
||||
public LocalizedString DisplayName { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
public dynamic Editor { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
namespace Orchard.ImportExport.ViewModels {
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.ImportExport.ViewModels {
|
||||
public class UploadRecipeViewModel {
|
||||
public UploadRecipeViewModel() {
|
||||
RecipeExecutionSteps = new List<RecipeExecutionStepViewModel>();
|
||||
}
|
||||
|
||||
public bool ResetSite { get; set; }
|
||||
public string SuperUserName { get; set; }
|
||||
public string SuperUserPassword { get; set; }
|
||||
public string SuperUserPasswordConfirmation { get; set; }
|
||||
public IList<RecipeExecutionStepViewModel> RecipeExecutionSteps { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
@model Orchard.ImportExport.ViewModels.RecipeBuilderViewModel
|
||||
@{
|
||||
Script.Require("ShapesBase");
|
||||
}
|
||||
@{
|
||||
var exportStepIndex = 0;
|
||||
foreach (var exportStep in Model.Steps) {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
@model Orchard.ImportExport.ViewModels.UploadRecipeViewModel
|
||||
@using Orchard.Utility.Extensions
|
||||
@model Orchard.ImportExport.ViewModels.UploadRecipeViewModel
|
||||
@{
|
||||
Script.Require("ShapesBase");
|
||||
}
|
||||
<p>
|
||||
@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" })))
|
||||
</p>
|
||||
@@ -9,7 +13,7 @@
|
||||
<fieldset>
|
||||
<div>
|
||||
@Html.CheckBoxFor(m => m.ResetSite)
|
||||
@Html.LabelFor(m => m.ResetSite, T("Reset Site").ToString(), new { @class = "forcheckbox" })
|
||||
@Html.LabelFor(m => m.ResetSite, T("Reset Site").ToString(), new {@class = "forcheckbox"})
|
||||
@Html.Hint(T("Check this option to reset your site before executing the uploaded recipe."))
|
||||
</div>
|
||||
<div data-controllerid="@Html.FieldIdFor(m => m.ResetSite)">
|
||||
@@ -20,9 +24,32 @@
|
||||
</div>
|
||||
<div>
|
||||
@Html.LabelFor(m => m.SuperUserPasswordConfirmation, T("Confirm Super User Password"))
|
||||
@Html.PasswordFor(m => m.SuperUserPasswordConfirmation, new { @class = "text medium" })
|
||||
@Html.PasswordFor(m => m.SuperUserPasswordConfirmation, new {@class = "text medium"})
|
||||
@Html.Hint(T("Repeat the password to make sure you didn't mistype anything."))
|
||||
</div>
|
||||
<div class="message message-Warning">@T("This will delete your database tables. Please consider creating a backup first.")</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
@{
|
||||
var stepIndex = 0;
|
||||
foreach (var step in Model.RecipeExecutionSteps) {
|
||||
var stepName = Html.NameFor(m => m.RecipeExecutionSteps[stepIndex].IsSelected).ToString();
|
||||
var stepId = stepName.HtmlClassify();
|
||||
|
||||
<fieldset class="recipe-builder-step recipe-builder-step-@step.Name.HtmlClassify()">
|
||||
<legend>
|
||||
<input type="hidden" name="@Html.NameFor(m => m.RecipeExecutionSteps[stepIndex].Name)" value="@Model.RecipeExecutionSteps[stepIndex].Name" />
|
||||
<input type="checkbox" id="@stepId" name="@stepName" value="true" />
|
||||
<label for="@stepId" class="forcheckbox">@step.DisplayName</label>
|
||||
</legend>
|
||||
@Html.Hint(step.Description)
|
||||
<div data-controllerid="@stepId">
|
||||
@Display(step.Editor)
|
||||
</div>
|
||||
</fieldset>
|
||||
stepIndex++;
|
||||
if (stepIndex < Model.RecipeExecutionSteps.Count) {
|
||||
<hr />
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Environment.Features;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Modules.Recipes.Executors {
|
||||
@@ -13,7 +14,10 @@ namespace Orchard.Modules.Recipes.Executors {
|
||||
_featureManager = featureManager;
|
||||
}
|
||||
|
||||
public override string Name { get { return "Feature"; } }
|
||||
public override string Name
|
||||
{
|
||||
get { return "Feature"; }
|
||||
}
|
||||
|
||||
// <Feature enable="f1,f2,f3" disable="f4" />
|
||||
// Enable/Disable features.
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<Compile Include="Providers\Builders\RecipeMetadataStep.cs" />
|
||||
<Compile Include="Providers\Builders\SettingsStep.cs" />
|
||||
<Compile Include="Providers\Executors\CommandStep.cs" />
|
||||
<Compile Include="ViewModels\ContentExecutionStepViewModel.cs" />
|
||||
<Compile Include="Providers\Executors\ContentStep.cs" />
|
||||
<Compile Include="Providers\Executors\ContentSchemaStep.cs" />
|
||||
<Compile Include="Providers\Executors\MigrationStep.cs" />
|
||||
@@ -142,6 +143,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\ExportSteps\Settings.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\ExecutionSteps\Content.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Orchard.Commands;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.Providers.Executors {
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Xml;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentTypes.Events;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.Providers.Executors {
|
||||
|
||||
@@ -3,8 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Recipes.ViewModels;
|
||||
|
||||
namespace Orchard.Recipes.Providers.Executors {
|
||||
public class ContentStep : RecipeExecutionStep {
|
||||
@@ -16,7 +19,35 @@ namespace Orchard.Recipes.Providers.Executors {
|
||||
_transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public override string Name { get { return "Content"; } }
|
||||
public override string Name
|
||||
{
|
||||
get { return "Content"; }
|
||||
}
|
||||
|
||||
public override LocalizedString DisplayName
|
||||
{
|
||||
get { return T("Content"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Provides additional coniguration for the Content recipe step."); }
|
||||
}
|
||||
|
||||
public override dynamic BuildEditor(dynamic shapeFactory) {
|
||||
return UpdateEditor(shapeFactory, null, null);
|
||||
}
|
||||
|
||||
public override dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater, UpdateRecipeExecutionStepContext context) {
|
||||
var viewModel = new ContentExecutionStepViewModel();
|
||||
|
||||
if (updater != null) {
|
||||
if (updater.TryUpdateModel(viewModel, Prefix, null, null)) {
|
||||
SetBatchSizeForDataStep(context.Step, viewModel.BatchSize);
|
||||
}
|
||||
}
|
||||
|
||||
return shapeFactory.EditorTemplate(TemplateName: "ExecutionSteps/Content", Model: viewModel, Prefix: Prefix);
|
||||
}
|
||||
|
||||
// <Data />
|
||||
// Import Data.
|
||||
@@ -93,6 +124,10 @@ namespace Orchard.Recipes.Providers.Executors {
|
||||
return elementDictionary;
|
||||
}
|
||||
|
||||
private void SetBatchSizeForDataStep(XElement step, int? batchSize) {
|
||||
step.SetAttributeValue("BatchSize", batchSize);
|
||||
}
|
||||
|
||||
private int GetBatchSizeForDataStep(XElement step) {
|
||||
int batchSize;
|
||||
if (step.Attribute("BatchSize") == null ||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.Providers.Executors {
|
||||
|
||||
@@ -6,6 +6,7 @@ using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Packaging.Models;
|
||||
using Orchard.Packaging.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.Providers.Executors {
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Xml.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Settings;
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.Recipes.ViewModels {
|
||||
public class ContentExecutionStepViewModel {
|
||||
public int? BatchSize { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@model Orchard.Recipes.ViewModels.ContentExecutionStepViewModel
|
||||
<div>
|
||||
@Html.LabelFor(m => m.BatchSize, T("Batch Size"))
|
||||
@Html.TextBoxFor(m => m.BatchSize, new { @class = "text small" })
|
||||
@Html.Hint(T("The batch size to use when importing the data. Leave empty to disable batched imports."))
|
||||
</div>
|
||||
@@ -6,6 +6,7 @@ using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Packaging.Models;
|
||||
using Orchard.Packaging.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Themes.Services;
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
<Compile Include="Recipes\Models\RecipeExecutionContext.cs" />
|
||||
<Compile Include="Recipes\Services\RecipeExecutionStep.cs" />
|
||||
<Compile Include="Recipes\Services\RecipeExecutor.cs" />
|
||||
<Compile Include="Recipes\Services\UpdateRecipeExecutionStepContext.cs" />
|
||||
<Compile Include="Reports\Report.cs" />
|
||||
<Compile Include="Reports\ReportEntry.cs" />
|
||||
<Compile Include="Reports\ReportExtentions.cs" />
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
namespace Orchard.Recipes.Models {
|
||||
public class RecipeExecutionContext {
|
||||
public string ExecutionId { get; set; }
|
||||
public RecipeStep RecipeStep { get; set; }
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
namespace Orchard.Recipes.Services {
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public interface IRecipeExecutionStep : IDependency {
|
||||
string Name { get; }
|
||||
LocalizedString DisplayName { get; }
|
||||
LocalizedString Description { get; }
|
||||
dynamic BuildEditor(dynamic shapeFactory);
|
||||
dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater, UpdateRecipeExecutionStepContext context);
|
||||
void Execute(RecipeExecutionContext context);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,31 @@
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public abstract class RecipeExecutionStep : Component, IRecipeExecutionStep {
|
||||
public abstract string Name { get; }
|
||||
|
||||
public virtual LocalizedString DisplayName {
|
||||
get { return T(Name); }
|
||||
}
|
||||
|
||||
public virtual LocalizedString Description {
|
||||
get { return DisplayName; }
|
||||
}
|
||||
|
||||
protected virtual string Prefix {
|
||||
get { return GetType().Name; }
|
||||
}
|
||||
|
||||
public virtual dynamic BuildEditor(dynamic shapeFactory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual dynamic UpdateEditor(dynamic shapeFactory, IUpdateModel updater, UpdateRecipeExecutionStepContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void Execute(RecipeExecutionContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class UpdateRecipeExecutionStepContext {
|
||||
public XDocument RecipeDocument { get; set; }
|
||||
public XElement Step { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user