From f15ccb70351861bad70e22c7c3dc7d04841b6200 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Fri, 26 Apr 2024 05:19:27 +0800 Subject: [PATCH] Export specific workflow (#11939) --- .../Controllers/WorkflowTypeController.cs | 54 +++++++++++++++++-- .../AllWorkflowTypeDeploymentSource.cs | 26 +++++---- .../OrchardCore.Workflows.csproj | 2 +- .../Scripting/WorkflowMethodsProvider.cs | 6 +-- .../ViewModels/WorkflowTypeIndexViewModel.cs | 1 + .../Views/WorkflowType/Edit.cshtml | 2 + .../Views/WorkflowType/Index.cshtml | 26 ++++++--- 7 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Controllers/WorkflowTypeController.cs b/src/OrchardCore.Modules/OrchardCore.Workflows/Controllers/WorkflowTypeController.cs index c6258ce73b5..960c9f025d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Controllers/WorkflowTypeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Controllers/WorkflowTypeController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO.Compression; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; @@ -13,12 +14,17 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using OrchardCore.Admin; +using OrchardCore.Deployment; +using OrchardCore.Deployment.Core.Services; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Notify; +using OrchardCore.Json; using OrchardCore.Navigation; +using OrchardCore.Recipes.Models; using OrchardCore.Routing; using OrchardCore.Workflows.Activities; +using OrchardCore.Workflows.Deployment; using OrchardCore.Workflows.Helpers; using OrchardCore.Workflows.Indexes; using OrchardCore.Workflows.Models; @@ -41,9 +47,9 @@ public class WorkflowTypeController : Controller private readonly IAuthorizationService _authorizationService; private readonly IActivityDisplayManager _activityDisplayManager; private readonly INotifier _notifier; - private readonly ISecurityTokenService _securityTokenService; private readonly IUpdateModelAccessor _updateModelAccessor; private readonly IShapeFactory _shapeFactory; + private readonly JsonSerializerOptions _documentJsonSerializerOptions; protected readonly IStringLocalizer S; protected readonly IHtmlLocalizer H; @@ -60,10 +66,10 @@ public WorkflowTypeController IActivityDisplayManager activityDisplayManager, IShapeFactory shapeFactory, INotifier notifier, - ISecurityTokenService securityTokenService, IStringLocalizer stringLocalizer, IHtmlLocalizer htmlLocalizer, - IUpdateModelAccessor updateModelAccessor) + IUpdateModelAccessor updateModelAccessor, + IOptions jsonSerializerOptions) { _pagerOptions = pagerOptions.Value; _session = session; @@ -74,11 +80,11 @@ public WorkflowTypeController _authorizationService = authorizationService; _activityDisplayManager = activityDisplayManager; _notifier = notifier; - _securityTokenService = securityTokenService; _updateModelAccessor = updateModelAccessor; _shapeFactory = shapeFactory; S = stringLocalizer; H = htmlLocalizer; + _documentJsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; } [Admin("Workflows/Types", "WorkflowTypes")] @@ -192,6 +198,9 @@ public async Task BulkEdit(WorkflowTypeIndexOptions options, IEnu { case WorkflowTypeBulkAction.None: break; + case WorkflowTypeBulkAction.Export: + return await ExportWorkflows(itemIds.ToArray()); + case WorkflowTypeBulkAction.Delete: foreach (var entry in checkedEntries) { @@ -213,7 +222,19 @@ public async Task BulkEdit(WorkflowTypeIndexOptions options, IEnu return RedirectToAction(nameof(Index)); } - public async Task EditProperties(long? id, string returnUrl = null) + [HttpPost] + public async Task Export(int id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageWorkflows)) + { + return Forbid(); + } + + return await ExportWorkflows(id); + } + + public async Task EditProperties(int? id, string returnUrl = null) + { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageWorkflows)) { @@ -546,5 +567,28 @@ private async Task BuildActivityDisplay(ActivityContext activityContext return activityShape; } + + private async Task ExportWorkflows(params long[] itemIds) + { + using var fileBuilder = new TemporaryFileBuilder(); + var archiveFileName = fileBuilder.Folder + ".zip"; + var recipeDescriptor = new RecipeDescriptor(); + var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, recipeDescriptor); + var workflowTypes = await _workflowTypeStore.GetAsync(itemIds); + + AllWorkflowTypeDeploymentSource.ProcessWorkflowType(deploymentPlanResult, workflowTypes, _documentJsonSerializerOptions); + + await deploymentPlanResult.FinalizeAsync(); + ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName); + + var packageName = itemIds.Length == 1 + ? workflowTypes.FirstOrDefault().Name + : S["Workflow Types"]; + + return new PhysicalFileResult(archiveFileName, "application/zip") + { + FileDownloadName = packageName + ".zip", + }; + } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Deployment/AllWorkflowTypeDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Workflows/Deployment/AllWorkflowTypeDeploymentSource.cs index 8b57675a1a1..312fd277090 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Deployment/AllWorkflowTypeDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Deployment/AllWorkflowTypeDeploymentSource.cs @@ -1,9 +1,11 @@ +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; using Microsoft.Extensions.Options; using OrchardCore.Deployment; using OrchardCore.Json; +using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; namespace OrchardCore.Workflows.Deployment @@ -28,23 +30,27 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan return; } - var data = new JsonArray(); - result.Steps.Add(new JsonObject - { - ["name"] = "WorkflowType", - ["data"] = data, - }); + ProcessWorkflowType(result, await _workflowTypeStore.ListAsync(), _jsonSerializerOptions); + } - foreach (var workflow in await _workflowTypeStore.ListAsync()) + public static void ProcessWorkflowType(DeploymentPlanResult result, IEnumerable workflowTypes, JsonSerializerOptions jsonSerializerOptions) + { + var data = new JsonArray(); + + foreach (var workflowType in workflowTypes) { - var objectData = JObject.FromObject(workflow, _jsonSerializerOptions); + var objectData = JObject.FromObject(workflowType, jsonSerializerOptions); // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql - objectData.Remove(nameof(workflow.Id)); + objectData.Remove(nameof(workflowType.Id)); data.Add(objectData); } - return; + result.Steps.Add(new JsonObject + { + ["name"] = "WorkflowType", + ["data"] = data, + }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/OrchardCore.Workflows.csproj b/src/OrchardCore.Modules/OrchardCore.Workflows/OrchardCore.Workflows.csproj index b45a448ce03..115f0978630 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/OrchardCore.Workflows.csproj +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/OrchardCore.Workflows.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Scripting/WorkflowMethodsProvider.cs b/src/OrchardCore.Modules/OrchardCore.Workflows/Scripting/WorkflowMethodsProvider.cs index 3e3f1a26f5b..c9aa4e90b20 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Scripting/WorkflowMethodsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Scripting/WorkflowMethodsProvider.cs @@ -28,7 +28,7 @@ public WorkflowMethodsProvider(WorkflowExecutionContext workflowContext) _workflowIdMethod = new GlobalMethod { Name = "workflowId", - Method = serviceProvider => (Func)(() => workflowContext.Workflow.WorkflowId), + Method = serviceProvider => () => workflowContext.Workflow.WorkflowId, }; _inputMethod = new GlobalMethod @@ -58,13 +58,13 @@ public WorkflowMethodsProvider(WorkflowExecutionContext workflowContext) _resultMethod = new GlobalMethod { Name = "lastResult", - Method = serviceProvider => (Func)(() => workflowContext.LastResult), + Method = serviceProvider => () => workflowContext.LastResult, }; _correlationIdMethod = new GlobalMethod { Name = "correlationId", - Method = serviceProvider => (Func)(() => workflowContext.Workflow.CorrelationId), + Method = serviceProvider => () => workflowContext.Workflow.CorrelationId, }; _setCorrelationIdMethod = new GlobalMethod diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/ViewModels/WorkflowTypeIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Workflows/ViewModels/WorkflowTypeIndexViewModel.cs index fb1d73b516a..5255e82ed04 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/ViewModels/WorkflowTypeIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/ViewModels/WorkflowTypeIndexViewModel.cs @@ -46,6 +46,7 @@ public enum WorkflowTypeFilter public enum WorkflowTypeBulkAction { None, + Export, Delete } } diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Edit.cshtml index 2b1e7abd904..f2ebc60005b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Edit.cshtml @@ -11,6 +11,7 @@ +
@@ -23,6 +24,7 @@
diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Index.cshtml index 46eb8815a1a..3c63c3e0cd3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Views/WorkflowType/Index.cshtml @@ -48,6 +48,7 @@ @T["Actions"]