Skip to content

Commit

Permalink
Add a way to restart a workflow instance (#14470)
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeAlhayek authored Oct 19, 2023
1 parent 566c21d commit 7966980
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Workflows;
using OrchardCore.Workflows.Abstractions.Models;
Expand All @@ -17,7 +18,10 @@ public abstract class ContentActivity : Activity
{
protected readonly IStringLocalizer S;

protected ContentActivity(IContentManager contentManager, IWorkflowScriptEvaluator scriptEvaluator, IStringLocalizer localizer)
protected ContentActivity(
IContentManager contentManager,
IWorkflowScriptEvaluator scriptEvaluator,
IStringLocalizer localizer)
{
ContentManager = contentManager;
ScriptEvaluator = scriptEvaluator;
Expand Down Expand Up @@ -68,6 +72,40 @@ public override ActivityExecutionResult Execute(WorkflowExecutionContext workflo
return Outcomes("Done");
}

public override async Task OnWorkflowRestartingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken = default)
{
ContentItem contentItem = null;

if (workflowContext.Input.TryGetValue(ContentEventConstants.ContentEventInputKey, out var contentEvent))
{
var contentEventContext = ((JObject)contentEvent).ToObject<ContentEventContext>();

if (contentEventContext?.ContentItemVersionId != null)
{
contentItem = await ContentManager.GetVersionAsync(contentEventContext.ContentItemVersionId);
}
if (contentItem == null && contentEventContext?.ContentItemId != null)
{
contentItem = await ContentManager.GetAsync(contentEventContext.ContentItemId);
}
}

if (contentItem == null && workflowContext.Input.TryGetValue(ContentEventConstants.ContentItemInputKey, out var contentItemEvent))
{
var item = ((JObject)contentItemEvent).ToObject<ContentItem>();

if (item?.ContentItemId != null)
{
contentItem = await ContentManager.GetAsync(item.ContentItemId);
}
}

if (contentItem != null)
{
workflowContext.Input[ContentEventConstants.ContentItemInputKey] = contentItem;
}
}

protected virtual async Task<IContent> GetContentAsync(WorkflowExecutionContext workflowContext)
{
IContent content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ private Task TriggerWorkflowEventAsync(string name, ContentItem contentItem)
{
Name = name,
ContentType = contentItem.ContentType,
ContentItemId = contentItem.ContentItemId
ContentItemId = contentItem.ContentItemId,
ContentItemVersionId = contentItem.ContentItemVersionId,
};

var input = new Dictionary<string, object>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.Locking.Distributed;
using OrchardCore.Mvc.Core.Utilities;
using OrchardCore.Navigation;
using OrchardCore.Routing;
Expand Down Expand Up @@ -42,6 +43,7 @@ public class WorkflowController : Controller
private readonly IUpdateModelAccessor _updateModelAccessor;
protected readonly dynamic New;
protected readonly IHtmlLocalizer H;
private readonly IDistributedLock _distributedLock;
protected readonly IStringLocalizer S;

public WorkflowController(
Expand All @@ -55,6 +57,7 @@ public WorkflowController(
IShapeFactory shapeFactory,
INotifier notifier,
IHtmlLocalizer<WorkflowController> htmlLocalizer,
IDistributedLock distributedLock,
IStringLocalizer<WorkflowController> stringLocalizer,
IUpdateModelAccessor updateModelAccessor)
{
Expand All @@ -69,6 +72,7 @@ public WorkflowController(
_updateModelAccessor = updateModelAccessor;
New = shapeFactory;
H = htmlLocalizer;
_distributedLock = distributedLock;
S = stringLocalizer;
}

Expand Down Expand Up @@ -242,6 +246,54 @@ public async Task<IActionResult> Delete(long id)
return RedirectToAction(nameof(Index), new { workflowTypeId = workflowType.Id });
}

[HttpPost]
public async Task<IActionResult> Restart(long id)
{
if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageWorkflows))
{
return Forbid();
}

var workflow = await _workflowStore.GetAsync(id);

if (workflow == null)
{
return NotFound();
}

var workflowType = await _workflowTypeStore.GetAsync(workflow.WorkflowTypeId);

if (workflowType == null)
{
return NotFound();
}

// If a singleton, try to acquire a lock per workflow type.
(var locker, var locked) = await _distributedLock.TryAcquireWorkflowTypeLockAsync(workflowType);
if (!locked)
{
await _notifier.ErrorAsync(H["Another instance is already running.", id]);
}
else
{
await using var acquiredLock = locker;

// Check if this is a workflow singleton and there's already an halted instance on any activity.
if (workflowType.IsSingleton && await _workflowStore.HasHaltedInstanceAsync(workflowType.WorkflowTypeId))
{
await _notifier.ErrorAsync(H["Another instance is already running.", id]);
}
else
{
await _workflowManager.RestartWorkflowAsync(workflow, workflowType);

await _notifier.SuccessAsync(H["Workflow {0} has been restarted.", id]);
}
}

return RedirectToAction(nameof(Index), new { workflowTypeId = workflowType.Id });
}

[HttpPost]
[ActionName(nameof(Index))]
[FormValueRequired("submit.BulkAction")]
Expand Down
Loading

0 comments on commit 7966980

Please sign in to comment.