Adding user tasks activity

--HG--
branch : 1.x
extra : rebase_source : 39247038049bcd4da2db305e1ce3c9e6f296cb79
This commit is contained in:
Sebastien Ros
2013-01-22 11:19:23 -08:00
parent 389eb0b3aa
commit 798df52811
31 changed files with 871 additions and 22 deletions

View File

@@ -14,20 +14,21 @@ namespace Orchard.Workflows.Activities {
public override bool CanExecute(ActivityContext context) {
try {
string contenttypes = context.State.ContentTypes;
var content = context.Tokens["Content"] as IContent;
string contentTypesState = context.State.ContentTypes;
// "" means 'any'
if (String.IsNullOrEmpty(contenttypes)) {
if (String.IsNullOrEmpty(contentTypesState)) {
return true;
}
string[] contentTypes = contentTypesState.Split(',');
var content = context.Tokens["Content"] as IContent;
if (content == null) {
return false;
}
var contentTypes = contenttypes.Split(new[] {','});
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
}
catch {

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Workflows.Models.Descriptors;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities {
public class IsInRoleActivity : BaseActivity {
private readonly IWorkContextAccessor _workContextAccessor;
public IsInRoleActivity(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string Name {
get { return "IsInRole"; }
}
public override LocalizedString Category {
get { return T("Conditions"); }
}
public override LocalizedString Description {
get { return T("Whether the current user is in a specific role."); }
}
public override string Form {
get { return "SelectRoles"; }
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
return new[] {T("Yes"), T("No")};
}
public override bool CanExecute(ActivityContext context) {
return true;
}
public override LocalizedString Execute(ActivityContext context) {
if (UserIsInRole(context)) {
return T("Yes");
}
return T("No");
}
private bool UserIsInRole(ActivityContext context) {
// checking if user is in an accepted role
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
var roles = GetRoles(context);
return UserIsInRole(user, roles);
}
public static bool UserIsInRole(IUser user, IEnumerable<string> roles) {
bool isInRole = false;
if (user == null) {
isInRole = roles.Contains("Anonymous");
}
else {
dynamic dynUser = user.ContentItem;
if (dynUser.UserRolesPart != null) {
IEnumerable<string> userRoles = dynUser.UserRolesPart.Roles;
isInRole = userRoles.Any(roles.Contains);
}
}
return isInRole;
}
private IEnumerable<string> GetRoles(ActivityContext context) {
if (context.State == null) {
return Enumerable.Empty<string>();
}
string roles = context.State.Roles;
if (String.IsNullOrEmpty(roles)) {
return Enumerable.Empty<string>();
}
return roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Core.Common.Models;
using Orchard.Events;
using Orchard.Messaging.Events;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Workflows.Models.Descriptors;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities.Mail {
public class MailActions : BaseActivity {
private readonly IMessageManager _messageManager;
private readonly IOrchardServices _orchardServices;
private readonly IMembershipService _membershipService;
public const string MessageType = "ActionEmail";
public MailActions(
IMessageManager messageManager,
IOrchardServices orchardServices,
IMembershipService membershipService) {
_messageManager = messageManager;
_orchardServices = orchardServices;
_membershipService = membershipService;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
return new[] { T("Sent") };
}
public override string Form {
get {
return "ActivityActionEmail";
}
}
public override LocalizedString Category {
get { return T("Messaging"); }
}
public override string Name {
get { return "SendEmail"; }
}
public override LocalizedString Description {
get { return T("Sends an e-mail to a specific user."); }
}
public override LocalizedString Execute(ActivityContext context) {
string recipient = context.State.Recipient;
var properties = new Dictionary<string, string>(); // context.State.Properties
properties.Add("Body", context.State.Body.ToString());
properties.Add("Subject", context.State.Subject.ToString());
if (recipient == "owner") {
var content = context.Tokens["Content"] as IContent;
if (content.Has<CommonPart>()) {
var owner = content.As<CommonPart>().Owner;
if (owner != null && owner.ContentItem != null && owner.ContentItem.Record != null) {
_messageManager.Send(owner.ContentItem.Record, MessageType, "email", properties);
}
_messageManager.Send(
SplitEmail(owner.As<IUser>().Email), MessageType, "email", properties);
}
}
else if (recipient == "author") {
var user = _orchardServices.WorkContext.CurrentUser;
// can be null if user is anonymous
if (user != null && String.IsNullOrWhiteSpace(user.Email)) {
_messageManager.Send(user.ContentItem.Record, MessageType, "email", properties);
}
}
else if (recipient == "admin") {
var username = _orchardServices.WorkContext.CurrentSite.SuperUser;
var user = _membershipService.GetUser(username);
// can be null if user is no super user is defined
if (user != null && !String.IsNullOrWhiteSpace(user.Email)) {
_messageManager.Send(user.ContentItem.Record, MessageType, "email", properties);
}
}
else if (recipient == "other") {
var email = properties["RecipientOther"];
_messageManager.Send(SplitEmail(email), MessageType, "email", properties);
}
return T("Sent");
}
private static IEnumerable<string> SplitEmail(string commaSeparated) {
return commaSeparated.Split(new[] { ',', ';' });
}
}
public class MailActionsHandler : IMessageEventHandler {
public MailActionsHandler() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void Sending(MessageContext context) {
if (context.MessagePrepared)
return;
if ((context.Recipients == null || !context.Recipients.Any()) &&
(context.Addresses == null || !context.Addresses.Any())) {
return;
}
switch (context.Type) {
case MailActions.MessageType:
context.MailMessage.Subject = context.Properties["Subject"];
context.MailMessage.Body = context.Properties["Body"];
FormatEmailBody(context);
context.MessagePrepared = true;
break;
}
}
private static void FormatEmailBody(MessageContext context) {
context.MailMessage.Body = "<p style=\"font-family:Arial, Helvetica; font-size:10pt;\">" + context.MailMessage.Body + "</p>";
}
public void Sent(MessageContext context) {
}
}
}

View File

@@ -0,0 +1,108 @@
using System;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization;
namespace Orchard.Workflows.Activities.Mail {
public class MailForms : IFormProvider {
protected dynamic Shape { get; set; }
public Localizer T { get; set; }
public MailForms(IShapeFactory shapeFactory) {
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public void Describe(DescribeContext context) {
Func<IShapeFactory, dynamic> form =
shape => Shape.Form(
Id: "ActionEmail",
_Type: Shape.FieldSet(
Title: T("Send to"),
_RecipientOwner: Shape.Radio(
Id: "recipient-owner",
Name: "Recipient",
Value: "owner",
Title: T("Owner"),
Description: T("The owner of the content item in context, such as a blog post's author.")
),
_RecipientAuthor: Shape.Radio(
Id: "recipient-author",
Name: "Recipient",
Value: "author",
Title: T("Author"),
Description: T("The current user when this action executes.")
),
_RecipientAdmin: Shape.Radio(
Id: "recipient-admin",
Name: "Recipient",
Value: "admin",
Title: T("Site Admin"),
Description: T("The site administrator.")
),
_RecipientOther: Shape.Radio(
Id: "recipient-other",
Name: "Recipient",
Value: "other",
Title: T("Other:")
),
_OtherEmails: Shape.Textbox(
Id: "recipient-other-email",
Name: "RecipientOther",
Title: T("E-mail"),
Description: T("Specify a comma-separated list of e-mail recipients."),
Classes: new[] { "large", "text", "tokenized" }
)
),
_Subject: Shape.Textbox(
Id: "Subject", Name: "Subject",
Title: T("Subject"),
Description: T("The subject of the e-mail."),
Classes: new[] { "large", "text", "tokenized" }),
_Message: Shape.Textarea(
Id: "Body", Name: "Body",
Title: T("Body"),
Description: T("The body of the e-mail."),
Classes: new[] { "tokenized" }
)
);
context.Form("ActivityActionEmail", form);
}
}
public class MailFormsValidator : IFormEventHandler {
public Localizer T { get; set; }
public void Building(BuildingContext context) {
}
public void Built(BuildingContext context) {
}
public void Validating(ValidatingContext context) {
if (context.FormName == "ActionEmail") {
if (context.ValueProvider.GetValue("Recipient").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Recipient", T("You must select at least one recipient").Text);
}
if (context.ValueProvider.GetValue("Subject").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Subject", T("You must provide a Subject").Text);
}
if (context.ValueProvider.GetValue("Body").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Body", T("You must provide a Body").Text);
}
if (context.ValueProvider.GetValue("RecipientOther").AttemptedValue == String.Empty &&
context.ValueProvider.GetValue("Recipient").AttemptedValue == "other") {
context.ModelState.AddModelError("Recipient", T("You must provide an e-mail address").Text);
}
}
}
public void Validated(ValidatingContext context) {
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities {
public class PublishActivity : BaseActivity {
private readonly IContentManager _contentManager;
public PublishActivity(IContentManager contentManager) {
_contentManager = contentManager;
}
public Localizer T { get; set; }
public override bool CanExecute(ActivityContext context) {
return true;
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
return new[] { T("Published") };
}
public override LocalizedString Execute(ActivityContext context) {
_contentManager.Publish(context.Content.ContentItem);
return T("Published");
}
public override string Name {
get { return "Publish"; }
}
public override LocalizedString Category {
get { return T("Content Items"); }
}
public override LocalizedString Description {
get { return T("Published the content item."); }
}
}
}

View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Workflows.Models.Descriptors;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities {
public class UserTaskActivity : BlockingActivity {
private readonly IWorkContextAccessor _workContextAccessor;
public UserTaskActivity(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string Name {
get { return "UserTask"; }
}
public override LocalizedString Category {
get { return T("Tasks"); }
}
public override LocalizedString Description {
get { return T("Wait for a user to execute a specific task."); }
}
public override string Form {
get { return "ActivityUserTask"; }
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
foreach (var action in GetActions(context)) {
yield return T(action);
}
}
public override bool CanExecute(ActivityContext context) {
return ActionIsValid(context) && UserIsInRole(context);
}
public override LocalizedString Execute(ActivityContext context) {
if (ActionIsValid(context) && UserIsInRole(context)) {
return T(context.Tokens["UserTask.Action"].ToString());
}
return null;
}
private bool UserIsInRole(ActivityContext context) {
// checking if user is in an accepted role
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
var roles = GetRoles(context);
return UserIsInRole(user, roles);
}
public static bool UserIsInRole(IUser user, IEnumerable<string> roles) {
bool isInRole = false;
if (user == null) {
isInRole = roles.Contains("Anonymous");
}
else {
dynamic dynUser = user.ContentItem;
if (dynUser.UserRolesPart != null) {
IEnumerable<string> userRoles = dynUser.UserRolesPart.Roles;
isInRole = userRoles.Any(roles.Contains);
}
}
return isInRole;
}
private bool ActionIsValid(ActivityContext context) {
// checking if user has triggered an accepted action
// triggered action
var userAction = context.Tokens["UserTask.Action"];
var actions = GetActions(context);
bool isValidAction = actions.Contains(userAction);
return isValidAction;
}
private IEnumerable<string> GetRoles(ActivityContext context) {
if (context.State == null) {
return Enumerable.Empty<string>();
}
string roles = context.State.Roles;
if (String.IsNullOrEmpty(roles)) {
return Enumerable.Empty<string>();
}
return roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
}
private IEnumerable<string> GetActions(ActivityContext context) {
if (context.State == null) {
return Enumerable.Empty<string>();
}
string actions = context.State.Actions;
if (String.IsNullOrEmpty(actions)) {
return Enumerable.Empty<string>();
}
return actions.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
}
}
}

View File

@@ -0,0 +1,118 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Data;
using Orchard.Forms.Services;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.UI.Notify;
using Orchard.Workflows.Activities;
using Orchard.Workflows.Models;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Drivers {
public class UserTaskDriver : ContentPartDriver<ContentPart> {
private readonly IWorkflowManager _workflowManager;
private readonly IRepository<AwaitingActivityRecord> _awaitingActivityRepository;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHttpContextAccessor _httpContextAccessor;
public UserTaskDriver(
IOrchardServices services,
IWorkflowManager workflowManager,
IRepository<AwaitingActivityRecord> awaitingActivityRepository,
IWorkContextAccessor workContextAccessor,
IHttpContextAccessor httpContextAccessor
) {
_workflowManager = workflowManager;
_awaitingActivityRepository = awaitingActivityRepository;
_workContextAccessor = workContextAccessor;
_httpContextAccessor = httpContextAccessor;
T = NullLocalizer.Instance;
Services = services;
}
public Localizer T { get; set; }
public IOrchardServices Services { get; set; }
protected override string Prefix {
get { return "UserTaskDriver"; }
}
protected override DriverResult Editor(ContentPart part, dynamic shapeHelper) {
var results = new List<DriverResult> {
ContentShape("UserTask_ActionButton", () => {
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 actions = awaiting.Where(x => {
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
string rolesState = state.Roles ?? "";
var roles = rolesState.Split(',').Select(role => role.Trim());
return UserTaskActivity.UserIsInRole(user, roles);
}).SelectMany(x => {
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
string actionState = state.Actions ?? "";
return actionState.Split(',').Select(action => action.Trim());
}).ToList();
return shapeHelper.UserTask_ActionButton().Actions(actions);
})
};
//if (part.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable) {}
return Combined(results.ToArray());
}
protected override DriverResult Editor(ContentPart part, IUpdateModel updater, dynamic shapeHelper) {
var httpContext = _httpContextAccessor.Current();
var name = httpContext.Request.Form["submit.Save"];
if (!string.IsNullOrEmpty(name) && name.StartsWith("usertask-")) {
name = name.Substring("usertask-".Length);
var user = Services.WorkContext.CurrentUser;
var awaiting = _awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
var actions = awaiting.Where(x => {
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
string rolesState = state.Roles ?? "";
var roles = rolesState.Split(',').Select(role => role.Trim());
return UserTaskActivity.UserIsInRole(user, roles);
}).SelectMany(x => {
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
string actionState = state.Actions ?? "";
return actionState.Split(',').Select(action => action.Trim());
}).ToList();
if (!actions.Contains(name)) {
Services.Notifier.Error(T("Not authorized to trigger {0}.", name));
}
else {
_workflowManager.TriggerEvent("UserTask", part, () => new Dictionary<string, object> { { "Content", part.ContentItem}, { "UserTask.Action", name } });
}
}
return Editor(part, shapeHelper);
}
//protected override DriverResult Display(ContentPart part, string displayType, dynamic shapeHelper) {
// return ContentShape("Parts_UserTask_SummaryAdmin", () => {
// var workContext = _workContextAccessor.GetContext();
// var user = workContext.CurrentUser;
// var awaiting = _awaitingActivityRepository.Table.Where(x => x.ContentItemRecord == part.ContentItem.Record && x.ActivityRecord.Name == "UserTask").ToList();
// awaiting = awaiting.Where(x => {
// var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
// string rolesState = state.Roles ?? "";
// var roles = rolesState.Split(',');
// return UserTaskActivity.UserIsInRole(user, roles);
// }).ToList();
// return shapeHelper.Parts_Workflow_SummaryAdmin().Activities(awaiting.Select(x => x.ActivityRecord));
// });
//}
}
}

View File

@@ -8,4 +8,4 @@ Description: Description for the module
Features:
Orchard.Workflows:
Description: Description for feature Orchard.Workflows.
Dependencies: Orchard.Tokens, Orchard.Forms, Orchard.jQuery
Dependencies: Orchard.Tokens, Orchard.Forms, Orchard.jQuery, Orchard.Roles

View File

@@ -72,8 +72,17 @@
<Content Include="Scripts\orchard-workflows-serialize.js" />
<Content Include="Scripts\orchard-workflows.js" />
<Content Include="Styles\admin-workflows.css" />
<Content Include="Styles\admin-usertask.css" />
<Content Include="Styles\workflows-activity-isinrole.css" />
<Content Include="Styles\workflows-activity-usertask.css" />
<Content Include="Styles\workflows-activity-notify.css" />
<Content Include="Styles\workflows-activity-publish.css" />
<Content Include="Styles\workflows-activity-sendemail.css" />
<Content Include="Styles\workflows-activity-contentremoved.css" />
<Content Include="Styles\workflows-activity-contentpublished.css" />
<Content Include="Styles\workflows-activity-contentversioned.css" />
<Content Include="Styles\workflows-activity-contentcreated.css" />
<Content Include="Styles\workflows-activity-decision.css" />
<Content Include="Styles\images\blocking.png" />
<Content Include="Web.config" />
<Content Include="Views\Web.config" />
<Content Include="Scripts\Web.config" />
@@ -94,6 +103,10 @@
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
<Name>Orchard.Forms</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Roles\Orchard.Roles.csproj">
<Project>{d10ad48f-407d-4db5-a328-173ec7cb010f}</Project>
<Name>Orchard.Roles</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Tokens\Orchard.Tokens.csproj">
<Project>{6f759635-13d7-4e94-bcc9-80445d63f117}</Project>
<Name>Orchard.Tokens</Name>
@@ -101,10 +114,16 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Activities\ContentActivity.cs" />
<Compile Include="Activities\IsInRoleActivity.cs" />
<Compile Include="Activities\Mail\MailActivity.cs" />
<Compile Include="Activities\Mail\MailForms.cs" />
<Compile Include="Activities\PublishActivity.cs" />
<Compile Include="Activities\UserTaskActivity.cs" />
<Compile Include="Activities\DecisionActivity.cs" />
<Compile Include="Activities\NotificationActivity.cs" />
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Drivers\UserTaskDriver.cs" />
<Compile Include="Drivers\WorkflowDriver.cs" />
<Compile Include="Handlers\ContentHandler.cs" />
<Compile Include="Handlers\WorkflowHandler.cs" />
@@ -121,6 +140,8 @@
<Compile Include="Models\WorkflowDefinitionRecord.cs" />
<Compile Include="Providers\ContentActivityProvider.cs" />
<Compile Include="Providers\ContentActivityForms.cs" />
<Compile Include="Providers\SelectRolesForms.cs" />
<Compile Include="Providers\UserTaskForms.cs" />
<Compile Include="Providers\NotificationActivityForms.cs" />
<Compile Include="Providers\NotificationActivityProvider.cs" />
<Compile Include="Services\BaseActivity.cs" />
@@ -168,6 +189,21 @@
<ItemGroup>
<Content Include="Views\Parts.Workflow.SummaryAdmin.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\UserTask.ActionButton.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-UserTask.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-IsInRole.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Styles\images\" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-SendEmail.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

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

View File

@@ -0,0 +1,51 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization;
using Orchard.Roles.Services;
namespace Orchard.Workflows.Providers {
public class SelectRolesForms : IFormProvider {
private readonly IRoleService _roleService;
protected dynamic Shape { get; set; }
public Localizer T { get; set; }
public SelectRolesForms(
IShapeFactory shapeFactory,
IRoleService roleService) {
_roleService = roleService;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public void Describe(DescribeContext context) {
Func<IShapeFactory, dynamic> form =
shape => {
var f = Shape.Form(
Id: "AnyOfRoles",
_Parts: Shape.SelectList(
Id: "role", Name: "Roles",
Title: T("Roles"),
Description: T("Select some roles."),
Size: 10,
Multiple: true
)
);
f._Parts.Add(new SelectListItem { Value = "", Text = T("Any").Text });
foreach (var role in _roleService.GetRoles().OrderBy(x => x.Name)) {
f._Parts.Add(new SelectListItem { Value = role.Name, Text = role.Name });
}
return f;
};
context.Form("SelectRoles", form);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement.MetaData;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization;
using Orchard.Roles.Services;
namespace Orchard.Workflows.Providers {
public class UserTaskForms : IFormProvider {
private readonly IRoleService _roleService;
protected dynamic Shape { get; set; }
public Localizer T { get; set; }
public UserTaskForms(
IShapeFactory shapeFactory,
IRoleService roleService) {
_roleService = roleService;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public void Describe(DescribeContext context) {
Func<IShapeFactory, dynamic> form =
shape => {
var f = Shape.Form(
Id: "AnyOfRoles",
_Parts: Shape.SelectList(
Id: "role", Name: "Roles",
Title: T("Roles"),
Description: T("Select some roles."),
Size: 10,
Multiple: true
),
_Message: Shape.Textbox(
Id: "actions", Name: "Actions",
Title: T("Available actions."),
Description: T("A comma separated list of actions."),
Classes: new[] {"textMedium"})
);
f._Parts.Add(new SelectListItem { Value = "", Text = T("Any").Text });
foreach (var role in _roleService.GetRoles().OrderBy(x => x.Name)) {
f._Parts.Add(new SelectListItem { Value = role.Name, Text = role.Name });
}
return f;
};
context.Form("ActivityUserTask", form);
}
}
}

View File

@@ -124,6 +124,11 @@
elt.endpoints = {};
var outcomes = activities[name].outcomes;
if (dom.data('outcomes')) {
outcomes = eval('[' + dom.data('outcomes') + ']');
}
for (i = 0; i < outcomes.length; i++) {
var ep = jsPlumb.addEndpoint(dom, {
anchor: "Continuous",

View File

@@ -74,12 +74,12 @@ namespace Orchard.Workflows.Services {
// evaluate processing condition
awaitingActivities = awaitingActivities.Where(a => {
var formatted = JsonConvert.DeserializeXNode(a.ActivityRecord.State).ToString();
var formatted = JsonConvert.DeserializeXNode(a.ActivityRecord.State, "Root").ToString();
var tokenized = _tokenizer.Replace(formatted, tokens);
var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized);
var workflowState = FormParametersHelper.FromJsonString(a.WorkflowRecord.State);
var context = new ActivityContext { Tokens = tokens, State = state, WorkflowState = workflowState, Content = target};
var context = new ActivityContext { Tokens = tokens, State = state.Root, WorkflowState = workflowState, Content = target };
// check the condition
try {
@@ -93,12 +93,12 @@ namespace Orchard.Workflows.Services {
// evaluate processing condition
startedWorkflows = startedWorkflows.Where(a => {
var formatted = JsonConvert.DeserializeXNode(a.State).ToString();
var formatted = JsonConvert.DeserializeXNode(a.State, "Root").ToString();
var tokenized = _tokenizer.Replace(formatted, tokens);
var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized);
var workflowState = FormParametersHelper.FromJsonString("{}");
var context = new ActivityContext { Tokens = tokens, State = state, WorkflowState = workflowState, Content = target };
var context = new ActivityContext { Tokens = tokens, State = state.Root, WorkflowState = workflowState, Content = target };
// check the condition
try {

View File

@@ -0,0 +1,5 @@
fieldset.usertask-button {
margin: 0 12px 0 0;
clear:none;
float:left;
}

View File

@@ -55,11 +55,11 @@
}
.blocking {
background-image: url(images/blocking.png) ;
background-position: top right;
background-repeat: no-repeat;
}
.blocking {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAKVJREFUOE+lkmENwyAQRusJA7WADiSgAA0oqAMUoAAr/QMh38oCbJQbLR3JS5rvjscFugD4CzKcgQxnKB/QWkMphRgjpJRgjJGs64p931uBMaY2bNuGEAI4583GQidwznVN1tqj9hkzcUiXJO0E1EnnpqFgVPzmsaDUfk15KUiM7qkRnJsSWYrzS+V1LUgIIeC9r/9KXu+9dcynkOEMZDgDGd4HywvvUq4US/BOrgAAAABJRU5ErkJggg==');
background-position: top right;
background-repeat: no-repeat;
}
.start {
background-color: #eee;
@@ -115,15 +115,17 @@
border: 1px solid #E4E5E6;
background-color: #F3F4F5;
padding: 0 5px;
height: 100px;
}
#activity-toolbox .activity-toolbox-item {
display: block;
padding: 0 10px;
width: 150px;
width: auto;
border: 1px solid #EAEAEA;
background-color: white;
margin: 5px 0;
margin: 5px 5px;
float: left;
}
#activity-toolbox .activity-toolbox-item h2 {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 B

View File

@@ -0,0 +1,7 @@
.isinrole {
width: 36px;
height: 36px;
background-image: url('data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAASAAAATgAAAAAAAABgAAAAAQAAAGAAAAABUGFpbnQuTkVUIHYzLjUuMTAA/9sAQwABAQEBAQEBAQEBAQEBAQECAgEBAQEDAgICAgMDBAQDAwMDBAQGBQQEBQQDAwUHBQUGBgYGBgQFBwcHBgcGBgYG/9sAQwEBAQEBAQEDAgIDBgQDBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYG/8AAEQgAIAAkAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/v4rwP8Aae/aW+E37IXwO8e/tB/GzXJdE8AfD/SvtF8tlCs19f3LkJbWNlCzKJbqeVkijQsq7mBdkQM6++V+D3/Bf/4ZW37S/wCx837OHw8+JHgQftJweLbDxx8OP2ernx1aWvizx1YaXDdpeWukaS0n2i/lWGW4mSOFGMklqEXL4FAHxH8HP+DrD4E+NvjPpngr4qfsxeMvhD8I9b8QGxs/i/bfE+DXrqxhkuFjtrzUtIFjB5MAjYyz+Rc3DxBWEaXBxn+rLT9QsdWsLLVNLvLXUdM1K0juNO1CxnWaG4gkUNHJG68MrKQQw4IIIr/IY+Ef7OHxv+OXxgsvgP8ADT4a+LNf+Kc+tPY6p4VGiTRTaO0U6w3U+qblH2G3tnb9/NcbI4ArGRlANf6pH7EuqfDKT9lv4JeD/hV8a/h3+0DoXwk+Gug+D9V+JXwx8dWniLTbzU9J063t7jN3bSOnmHarlWIYCRSQM0AfVVFFFACMwVSx6KOa/wAkX9tb4/8Aiz9pv9rL49fHTxZrd7rOp+N/ibqc2k3VyWVrfSYZjDpttGp5SOC0ht4lXsIx3r/W6IBBBGQRyK/ks+O//Bqd8NfiH8XPHfjn4Rftcax8HfAPizXptQ0X4aa18D18TSaO053zW8WoLrFqZIFlaTyleHekexHeVlMjAH8h3jz9tf8AbL+KfhHVvh/8Tv2tv2m/iN4C16OFNd8EePPj1rmsaRepFIskSz2dxdvFIEkijdQynDIpHIBr9jP+DZn4/wDif4a/8FDIPgzb6zeReCv2ivh5rFjrvh4Atb3GqaRazahYXLL0WSKOC/RWP8NxIP4hX6E/8Qjn/WQT/wA1R/8Awlr9Hf8AgmV/wb+fDT/gnx8en/aN8U/HrV/2gfiDoGiXFn8MYo/huvhXT9Dku4ZIL66li+33b3U728pijO+JI1km3JKzxtEAf0J0UUUAf//Z');
background-repeat: no-repeat;
background-position: center;
}

View File

@@ -0,0 +1,7 @@
.envelope {
width: 36px;
height: 36px;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAARhJREFUSEvtlkEORTAQhnsnSxsre+dwBCdwBidwAyewl0jsbJ3Ahsi8jvfK6JsnqGLxFp/IaGf+jr9NBQDcChu8kumlbVsIggAcx7GK53mAtRYChmEQURSxE2wQhiF0XTcL6PteXLF6xacLD+oAkmUZO9gGaZrKkpoJkSRJ2AlnEsexLDXXXAhAbIrQiyPjA01YFIV8fQfLsmQTmJDn+ZS/qqqlCdUuoArPMiY1HIId/toFdBu6rgt1XcuweTfoqpumAd/3x/iqAAV16t5zQj/t9B22SQAZ+DMRxxbhmwUoaFLaSor+69bE7haAcGZS3/aa95AABTWWzlbDGglAaALT+YcSmPIX8DwBj7iQyC7cdym9EzZ4HSBeOGnowJmFUWAAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: center;
}

View File

@@ -0,0 +1,12 @@
@using Orchard.Utility.Extensions
@{
string name = Model.Name;
bool blocking = Model.IsBlocking;
}
<div class="@blocking" title="@Model.State.Roles">
<div class="isinrole" ></div>
@*@name.CamelFriendly()*@
</div>

View File

@@ -0,0 +1,7 @@
@using Orchard.Utility.Extensions
<div>
<div class="envelope" ></div>
@*@name.CamelFriendly()*@
</div>

View File

@@ -0,0 +1,12 @@
@using Orchard.Utility.Extensions
@{
string name = Model.Name;
string actions = Model.State.Actions;
var outcomes = String.Join(",", actions == null ? new string[0] : actions.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries).Select(x => "'" + x.Trim() + "'").ToArray());
}
<div class="blocking" data-outcomes="@outcomes">
@name.CamelFriendly()
</div>

View File

@@ -11,7 +11,7 @@
<div id="activity-toolbox">
<ul>
@foreach (var activity in allActivities) {
<li class="activity-toolbox-item" data-activity-name="@activity.Name">
<li class="activity-toolbox-item" data-activity-name="@activity.Name" title="@activity.Description">
<h2>@activity.Name.CamelFriendly()</h2>
</li>
@@ -26,9 +26,9 @@
//<![CDATA[
var activities = { @foreach (var activity in allActivities) { <text>
'@activity.Name': {
outcomes: [ @Html.Raw("'" + String.Join("', '", activity.GetPossibleOutcomes(new ActivityContext()).Select(x => HttpUtility.JavaScriptStringEncode(x.Text)).ToArray()) + "'")],
category: '@activity.Category.Text',
description: '@activity.Description.Text',
outcomes: [@Html.Raw(String.Join(",", activity.GetPossibleOutcomes(new ActivityContext()).Where(x => !String.IsNullOrEmpty(x.Text)).Select(x => "'" + HttpUtility.JavaScriptStringEncode(x.Text) + "'").ToArray()))],
category: '@HttpUtility.JavaScriptStringEncode(activity.Category.Text)',
description: '@HttpUtility.JavaScriptStringEncode(activity.Description.Text)',
isBlocking: @(activity.IsBlocking ? "true" : "false"),
hasForm: @(!String.IsNullOrWhiteSpace(activity.Form) ? "true" : "false")
},</text>

View File

@@ -0,0 +1,10 @@
@{
Style.Include("admin-usertask");
IEnumerable<string> actions = Model.Actions;
}
<fieldset class="usertask-button">
@foreach (var action in actions) {
<button type="submit" name="submit.Save" value="usertask-@action">@T(action)</button>
}
</fieldset>