Adding running workflows list view

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2013-02-05 10:37:07 -08:00
parent 3ac520fb16
commit 91070602bc
19 changed files with 120 additions and 41 deletions

View File

@@ -120,8 +120,8 @@ namespace Orchard.Workflows.Activities {
foreach (var action in awaiting) {
var tokens = new Dictionary<string, object> { { "Content", _contentManager.Get(action.ContentItemRecord.Id, VersionOptions.Latest) } };
var contentItem = _contentManager.Get(action.ContentItemRecord.Id, VersionOptions.Latest);
var tokens = new Dictionary<string, object> { { "Content", _contentManager.Get(action.WorkflowRecord.ContentItemRecord.Id, VersionOptions.Latest) } };
var contentItem = _contentManager.Get(action.WorkflowRecord.ContentItemRecord.Id, VersionOptions.Latest);
var workflowState = FormParametersHelper.FromJsonString(action.WorkflowRecord.State);
workflowState.TimerActivity_StartedUtc = null;
action.WorkflowRecord.State = FormParametersHelper.ToJsonString(workflowState);

View File

@@ -25,6 +25,7 @@ namespace Orchard.Workflows.Controllers {
public class AdminController : Controller, IUpdateModel {
private readonly ISiteService _siteService;
private readonly IRepository<WorkflowDefinitionRecord> _workflowDefinitionRecords;
private readonly IRepository<WorkflowRecord> _workflowRecords;
private readonly IActivitiesManager _activitiesManager;
private readonly IFormManager _formManager;
@@ -33,11 +34,13 @@ namespace Orchard.Workflows.Controllers {
IShapeFactory shapeFactory,
ISiteService siteService,
IRepository<WorkflowDefinitionRecord> workflowDefinitionRecords,
IRepository<WorkflowRecord> workflowRecords,
IActivitiesManager activitiesManager,
IFormManager formManager
) {
_siteService = siteService;
_workflowDefinitionRecords = workflowDefinitionRecords;
_workflowRecords = workflowRecords;
_activitiesManager = activitiesManager;
_formManager = formManager;
Services = services;
@@ -107,6 +110,25 @@ namespace Orchard.Workflows.Controllers {
return View(model);
}
public ActionResult List(int id) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list workflows")))
return new HttpUnauthorizedResult();
var contentItem = Services.ContentManager.Get(id, VersionOptions.Latest);
if (contentItem == null) {
return HttpNotFound();
}
var workflows = _workflowRecords.Table.Where(x => x.ContentItemRecord == contentItem.Record).ToList();
var viewModel = New.ViewModel(
ContentItem: contentItem,
Workflows: workflows
);
return View(viewModel);
}
public ActionResult Create() {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to create workflows")))
return new HttpUnauthorizedResult();
@@ -268,11 +290,14 @@ namespace Orchard.Workflows.Controllers {
return HttpNotFound();
}
IShape shape = New.Activity(activity);
dynamic shape = New.Activity(activity);
if (model.State != null) {
var dynamicState = FormParametersHelper.ToDynamic(FormParametersHelper.ToString(model.State));
((dynamic)shape).State(dynamicState);
var state = FormParametersHelper.ToDynamic(FormParametersHelper.ToString(model.State));
shape.State(state);
}
else {
shape.State(FormParametersHelper.FromJsonString("{}"));
}
shape.Metadata.Alternates.Add("Activity__" + activity.Name);

View File

@@ -47,7 +47,7 @@ namespace Orchard.Workflows.Drivers {
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
var awaiting = _awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
var awaiting = _awaitingActivityRepository.Table.Where(x => x.WorkflowRecord.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
var actions = awaiting.Where(x => UserIsInRole(x, user)).SelectMany(ListAction).ToList();
return shapeHelper.UserTask_ActionButton().Actions(actions);
@@ -87,7 +87,7 @@ namespace Orchard.Workflows.Drivers {
var user = Services.WorkContext.CurrentUser;
var awaiting = _awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
var awaiting = _awaitingActivityRepository.Table.Where(x => x.WorkflowRecord.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
var actions = awaiting.Where(x => UserIsInRole(x, user)).SelectMany(ListAction).ToList();
if (!actions.Contains(name)) {

View File

@@ -7,13 +7,13 @@ using Orchard.Workflows.Models;
namespace Orchard.Workflows.Drivers {
public class WorkflowDriver : ContentPartDriver<CommonPart> {
private readonly IRepository<AwaitingActivityRecord> _awaitingActivityRepository;
private readonly IRepository<WorkflowRecord> _workflowRepository;
public WorkflowDriver(
IOrchardServices services,
IRepository<AwaitingActivityRecord> awaitingActivityRepository
IRepository<WorkflowRecord> workflowRepository
) {
_awaitingActivityRepository = awaitingActivityRepository;
_workflowRepository = workflowRepository;
T = NullLocalizer.Instance;
Services = services;
}
@@ -27,8 +27,8 @@ namespace Orchard.Workflows.Drivers {
protected override DriverResult Display(CommonPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Workflow_SummaryAdmin", () => {
var awaiting = _awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record).ToList();
return shapeHelper.Parts_Workflow_SummaryAdmin().Activities(awaiting.Select(x => x.ActivityRecord));
var workflows = _workflowRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record).Distinct().ToList();
return shapeHelper.Parts_Workflow_SummaryAdmin().Workflows(workflows);
});
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
@@ -10,17 +9,16 @@ namespace Orchard.Workflows.Handlers {
public class WorkflowHandler : ContentHandler {
public WorkflowHandler(
IRepository<AwaitingActivityRecord> awaitingActivityRepository,
IRepository<WorkflowRecord> workflowRepository
) {
// Delete any pending workflow related to a deleted content item
OnRemoving<ContentPart>(
(context, part) => {
var awaiting = awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == context.ContentItemRecord).ToList();
var workflows = workflowRepository.Table.Where(x => x.ContentItemRecord == context.ContentItemRecord).ToList();
foreach (var item in awaiting) {
workflowRepository.Delete(item.WorkflowRecord);
foreach (var item in workflows) {
workflowRepository.Delete(item);
}
});
}

View File

@@ -19,7 +19,8 @@ namespace Orchard.Workflows {
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("State", column => column.Unlimited())
.Column<int>("WorkflowDefinitionRecord_id")
);
.Column<int>("ContentItemRecord_id")
);
// Creating table WorkflowDefinitionRecord
SchemaBuilder.CreateTable("WorkflowDefinitionRecord", table => table
@@ -32,7 +33,6 @@ namespace Orchard.Workflows {
SchemaBuilder.CreateTable("AwaitingActivityRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<int>("ActivityRecord_id")
.Column<int>("ContentItemRecord_id")
.Column<int>("WorkflowRecord_id")
);

View File

@@ -1,13 +1,9 @@
using Orchard.ContentManagement.Records;
namespace Orchard.Workflows.Models {
namespace Orchard.Workflows.Models {
public class AwaitingActivityRecord {
public virtual int Id { get; set; }
public virtual ActivityRecord ActivityRecord { get; set; }
public virtual ContentItemRecord ContentItemRecord { get; set; }
// Parent property
public virtual WorkflowRecord WorkflowRecord { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Orchard.ContentManagement.Records;
using Orchard.Data.Conventions;
namespace Orchard.Workflows.Models {
@@ -25,6 +26,11 @@ namespace Orchard.Workflows.Models {
[CascadeAllDeleteOrphan]
public virtual IList<AwaitingActivityRecord> AwaitingActivities { get; set; }
/// <summary>
/// The associated content item
/// </summary>
public virtual ContentItemRecord ContentItemRecord { get; set; }
/// <summary>
/// Parent <see cref="WorkflowDefinitionRecord"/>
/// </summary>

View File

@@ -4,6 +4,7 @@ Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.0
OrchardVersion: 1.0
Category: Content
Description: Description for the module
Features:
Orchard.Workflows:

View File

@@ -71,6 +71,7 @@
<Content Include="Scripts\jquery.jsPlumb-1.3.16-all-min.js" />
<Content Include="Scripts\orchard-workflows-serialize.js" />
<Content Include="Scripts\orchard-workflows.js" />
<Content Include="Styles\images\cog.png" />
<Content Include="Styles\workflows-activity-delete-publish.css" />
<Content Include="Styles\workflows-activity-timer.css" />
<Content Include="Styles\workflows-activity-branch.css" />
@@ -203,7 +204,7 @@
<Content Include="Views\Activity-IsInRole.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Styles\images\" />
<Content Include="Views\Admin\List.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-SendEmail.cshtml" />

View File

@@ -1,6 +1,6 @@
<Placement>
<Match DisplayType="SummaryAdmin">
<Place Parts_Workflow_SummaryAdmin="Meta:5"/>
<Place Parts_Workflow_SummaryAdmin="Meta:2"/>
</Match>
<Place UserTask_ActionButton="Sidebar:24.1"/>

View File

@@ -66,7 +66,7 @@ namespace Orchard.Workflows.Services {
// it's important to return activities at this point as a workflow could be awaiting
// on several ones. When an activity is restarted, all the other ones of the same workflow are cancelled.
awaitingActivities.AddRange(_awaitingActivityRepository.Table.Where(
x => x.ActivityRecord.Name == name && x.ActivityRecord.Start == false && x.ContentItemRecord == target.ContentItem.Record
x => x.ActivityRecord.Name == name && x.ActivityRecord.Start == false && x.WorkflowRecord.ContentItemRecord == target.ContentItem.Record
).ToList()
);
@@ -139,7 +139,8 @@ namespace Orchard.Workflows.Services {
// workflow halted, create a workflow state
var workflow = new WorkflowRecord {
WorkflowDefinitionRecord = activityRecord.WorkflowDefinitionRecord,
State = "{}"
State = "{}",
ContentItemRecord = workflowContext.Content.ContentItem.Record
};
workflowContext.Record = workflow;
@@ -169,7 +170,6 @@ namespace Orchard.Workflows.Services {
foreach (var blocking in blockedOn) {
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
ActivityRecord = blocking,
ContentItemRecord = workflowContext.Content.ContentItem.Record
});
}
}
@@ -205,7 +205,6 @@ namespace Orchard.Workflows.Services {
foreach (var blocking in blockedOn) {
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
ActivityRecord = blocking,
ContentItemRecord = workflowContext.Content.ContentItem.Record
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

View File

@@ -2,8 +2,12 @@
@{
string name = Model.Name;
string notification = Model.State.Notification;
string message = Model.State.Message;
string description = String.IsNullOrEmpty(notification) ? "" : T("{0}: {1}", notification, message).ToString();
}
<div class="activity-notify" title="@Model.State.Notification: @Model.State.Message">
<div class="activity-notify" title="@description">
<div>@name.CamelFriendly()</div>
</div>

View File

@@ -74,7 +74,7 @@
@Html.Hidden("localId", Model.LocalId)
@Html.Hidden("data", String.Empty)
@Html.Hidden("confirm-delete-activity", T("Delete activity ?"))
@Html.Hidden("confirm-delete-activity", T("Are you sure you want to remove this activity?"))
using (Script.Foot()) {
<script type="text/javascript">

View File

@@ -1,5 +1,4 @@
@model Orchard.Workflows.ViewModels.AdminIndexViewModel
@using Orchard.Workflows.Models;
@model AdminIndexViewModel
@using Orchard.Workflows.ViewModels;
@{
@@ -12,10 +11,10 @@
}
}
<h1>@Html.TitleForPage(T("Manage Workflows").ToString()) </h1>
<h1>@Html.TitleForPage(T("Manage Workflow Definitions").ToString()) </h1>
@using (Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()
<div class="manage">@Html.ActionLink(T("Create new Wokflow").ToString(), "Create", new { Area = "Orchard.Workflows", returnurl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })</div>
<div class="manage">@Html.ActionLink(T("Create new Wokflow Definition").ToString(), "Create", new { Area = "Orchard.Workflows", returnurl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })</div>
<fieldset class="bulk-actions">
<label for="publishActions">@T("Actions:")</label>
@@ -46,6 +45,7 @@
<tr>
<th scope="col" class="checkbox">&nbsp;&darr;</th>
<th scope="col">@T("Name")</th>
<th scope="col">&nbsp;</th>
<th scope="col" class="actions">&nbsp;</th>
</tr>
</thead>
@@ -58,6 +58,12 @@
<td>
@Html.ActionLink(entry.Name, "Edit", new { id = entry.WokflowDefinitionId })
</td>
<td>
@if (entry.WorkflowDefinitionRecord.WorkflowRecords.Any()) {
@T("{0} Running", entry.WorkflowDefinitionRecord.WorkflowRecords.Count)
}
&nbsp;
</td>
<td>
@* TODO: As WD is not a Content Item, an EditProperties action is necessary @Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.WokflowDefinitionId, returnurl = HttpContext.Current.Request.RawUrl }) |*@
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.WokflowDefinitionId }) |

View File

@@ -0,0 +1,38 @@
@using Orchard.Utility.Extensions
@using Orchard.Workflows.Models
<h1>@Html.TitleForPage(T("Running workflows").ToString()) </h1>
<fieldset>
<table class="items">
<thead>
<tr>
<th scope="col" class="checkbox">&nbsp;&darr;</th>
<th scope="col">@T("Worfklow Definition")</th>
<th scope="col">Blocking activities</th>
<th scope="col" class="actions">&nbsp;</th>
</tr>
</thead>
@foreach (WorkflowRecord workflow in Model.Workflows) {
<tr>
<td>
&nbsp;
</td>
<td>
@Html.ActionLink(workflow.WorkflowDefinitionRecord.Name, "Edit", new { id = workflow.WorkflowDefinitionRecord.Id })
</td>
<td>
@foreach (AwaitingActivityRecord awaiting in workflow.AwaitingActivities) {
@awaiting.ActivityRecord.Name.CamelFriendly()
<br/>
}
&nbsp;
</td>
<td>
@*@Html.ActionLink(T("Edit").ToString(), "EditWorkflow", new { id = workflow.Id })*@ |
@*@Html.ActionLink(T("Delete").ToString(), "DeleteWorkflow", new { id = workflow.Id }, new { itemprop = "RemoveUrl UnsafeUrl" })*@
</td>
</tr>
}
</table>
</fieldset>

View File

@@ -1,10 +1,14 @@
@{
IEnumerable<Orchard.Workflows.Models.ActivityRecord> activities = Model.Activities;
IEnumerable<Orchard.Workflows.Models.WorkflowRecord> activities = Model.Workflows;
var count = activities.Count();
}
@if (count > 0) {
<div>
@T("{0} active workflow(s)", count)
</div>
<ul class="pageStatus">
<li title="@T("Running workflows")" >
<img class="icon" src="@Href("~/Modules/Orchard.Workflows/Styles/Images/cog.png")" alt="@T("Running ({0})", count)" />
@T("Running ({0})", count)
&nbsp;|&nbsp;
</li>
</ul>
}

View File

@@ -5,6 +5,7 @@ Website: http://orchardproject.net
Version: 1.6
OrchardVersion: 1.6
Description: Description for the module
Category: Developer
Features:
UpgradeTo16:
Description: Description for feature UpgradeTo16.