Engine working

--HG--
branch : 1.x
extra : rebase_source : eee366cd0694d27ac015129ac82d4dc702a19c5f
This commit is contained in:
Sebastien Ros
2013-01-18 16:22:11 -08:00
parent d26bfaf836
commit efde29add3
19 changed files with 506 additions and 42 deletions

View File

@@ -12,21 +12,26 @@ namespace Orchard.Workflows.Activities {
public Localizer T { get; set; }
public override bool CanExecute(ActivityContext context) {
string contenttypes = context.State.ContentTypes;
var content = context.Tokens["Content"] as IContent;
try {
string contenttypes = context.State.ContentTypes;
var content = context.Tokens["Content"] as IContent;
// "" means 'any'
if (String.IsNullOrEmpty(contenttypes)) {
return true;
// "" means 'any'
if (String.IsNullOrEmpty(contenttypes)) {
return true;
}
if (content == null) {
return false;
}
var contentTypes = contenttypes.Split(new[] {','});
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
}
if (content == null) {
catch {
return false;
}
var contentTypes = contenttypes.Split(new[] { ',' });
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
@@ -34,7 +39,13 @@ namespace Orchard.Workflows.Activities {
}
public override LocalizedString Execute(ActivityContext context) {
return T("True");
return T("Success");
}
public override string Form {
get {
return "SelectContentTypes";
}
}
}

View File

@@ -146,6 +146,21 @@ namespace Orchard.Workflows.Controllers {
return View(viewModel);
}
[HttpPost]
public ActionResult Delete(int id) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
return new HttpUnauthorizedResult();
var workflowDefinition = _workflowDefinitionRecords.Get(id);
if (workflowDefinition != null) {
_workflowDefinitionRecords.Delete(workflowDefinition);
Services.Notifier.Information(T("Workflow {0} deleted", workflowDefinition.Name));
}
return RedirectToAction("Index");
}
private WorkflowDefinitionViewModel CreateWorkflowDefinitionViewModel(WorkflowDefinitionRecord workflowDefinitionRecord) {
if (workflowDefinitionRecord == null) {
throw new ArgumentNullException("workflowDefinitionRecord");
@@ -162,6 +177,7 @@ namespace Orchard.Workflows.Controllers {
activity.ClientId = x.Name + "_" + x.Id;
activity.Left = x.X;
activity.Top = x.Y;
activity.Start = x.Start;
activity.State = FormParametersHelper.FromJsonString(x.State);
return activity;
@@ -193,6 +209,8 @@ namespace Orchard.Workflows.Controllers {
return HttpNotFound();
}
workflowDefinitionRecord.Enabled = true;
var state = FormParametersHelper.FromJsonString(data);
var activitiesIndex = new Dictionary<string, ActivityRecord>();
@@ -205,6 +223,7 @@ namespace Orchard.Workflows.Controllers {
Name = activity.Name,
X = activity.Left,
Y = activity.Top,
Start = activity.Start,
State = FormParametersHelper.ToJsonString(activity.State),
WorkflowDefinitionRecord = workflowDefinitionRecord
});

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Handlers {
public class RulePartHandler : ContentHandler {
public RulePartHandler(IWorkflowManager workflowManager) {
OnPublished<ContentPart>(
(context, part) =>
workflowManager.TriggerEvent("ContentPublished",
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
OnRemoved<ContentPart>(
(context, part) =>
workflowManager.TriggerEvent("ContentRemoved",
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
OnVersioned<ContentPart>(
(context, part1, part2) =>
workflowManager.TriggerEvent("ContentVersioned",
() => new Dictionary<string, object> { { "Content", part1.ContentItem } }));
OnCreated<ContentPart>(
(context, part) =>
workflowManager.TriggerEvent("ContentCreated",
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
}
}
}

View File

@@ -32,7 +32,8 @@ namespace Orchard.Workflows {
SchemaBuilder.CreateTable("AwaitingActivityRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<int>("ActivityRecord_id")
);
.Column<int>("WorkflowRecord_id")
);
// Creating table ActivityRecord
SchemaBuilder.CreateTable("ActivityRecord", table => table
@@ -41,10 +42,12 @@ namespace Orchard.Workflows {
.Column<int>("X")
.Column<int>("Y")
.Column<string>("State", c => c.Unlimited())
.Column<bool>("Start")
.Column<int>("WorkflowDefinitionRecord_id")
);
return 1;
}
}
}

View File

@@ -28,6 +28,11 @@ namespace Orchard.Workflows.Models {
/// </summary>
public virtual int Y { get; set; }
/// <summary>
/// Whether the activity is a start state for a WorkflowDefinition.
/// </summary>
public virtual bool Start { get; set; }
/// <summary>
/// The parent <see cref="WorkflowDefinitionRecord"/>
/// containing this activity.

View File

@@ -7,6 +7,6 @@ namespace Orchard.Workflows.Models {
public virtual ActivityRecord ActivityRecord { get; set; }
// Parent property
public virtual IList<WorkflowRecord> WorkflowRecords { get; set; }
public virtual WorkflowRecord WorkflowRecord { get; set; }
}
}

View File

@@ -72,6 +72,7 @@
<Content Include="Scripts\orchard-workflows-serialize.js" />
<Content Include="Scripts\orchard-workflows.js" />
<Content Include="Styles\admin-workflows.css" />
<Content Include="Styles\workflows-activity-decision.css" />
<Content Include="Styles\images\blocking.png" />
<Content Include="Web.config" />
<Content Include="Views\Web.config" />
@@ -104,6 +105,7 @@
<Compile Include="Activities\NotificationActivity.cs" />
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Handlers\ContentHandler.cs" />
<Compile Include="Models\AwaitingActivityRecord.cs" />
<Compile Include="Models\Descriptors\ActivityContext.cs" />
<Compile Include="Models\Descriptors\ActivityDescriptor.cs" />
@@ -125,13 +127,13 @@
<Compile Include="Services\IActivityProvider.cs" />
<Compile Include="Services\ActivitiesManager.cs" />
<Compile Include="Services\IActivitiesManager.cs" />
<Compile Include="Services\IWorkflowManager.cs" />
<Compile Include="Services\WorkflowManager.cs" />
<Compile Include="ViewModels\AdminEditViewModel.cs" />
<Compile Include="ViewModels\AdminIndexViewModel.cs" />
<Compile Include="ViewModels\WorkflowDefinitionViewModel.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Handlers\" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Views\Admin\Index.cshtml" />
</ItemGroup>
@@ -156,6 +158,9 @@
<ItemGroup>
<Content Include="Views\Activity-Notify.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-Decision.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -27,7 +27,7 @@ namespace Orchard.Workflows.Providers {
var f = Shape.Form(
Id: "AnyOfContentTypes",
_Parts: Shape.SelectList(
Id: "contenttypes", Name: "contenttypes",
Id: "contenttypes", Name: "ContentTypes",
Title: T("Content types"),
Description: T("Select some content types."),
Size: 10,

View File

@@ -12,6 +12,7 @@ var saveLocal = function (localId) {
workflow.Activities.push({
Name: activity.viewModel.name,
ClientId: activity.viewModel.clientId,
Start: activity.viewModel.start,
State: activity.viewModel.state,
Left: $(activity).position().left,
Top: $(activity).position().top
@@ -63,7 +64,7 @@ var loadActivities = function (localId) {
// activities
for (var i = 0; i < workflow.Activities.length; i++) {
var activity = workflow.Activities[i];
renderActivity(activity.ClientId, activity.Name, activity.State, activity.Top, activity.Left);
renderActivity(activity.ClientId, activity.Name, activity.State, activity.Start, activity.Top, activity.Left);
}
// connections

View File

@@ -1,19 +1,19 @@
 var connectorPaintStyle = {
lineWidth: 3,
strokeStyle: "grey",
lineWidth: 2,
strokeStyle: "#999",
joinstyle: "round",
//outlineColor: "white",
//outlineWidth: 7
};
var connectorHoverStyle = {
lineWidth: 3,
strokeStyle: "#2e2aF8"
lineWidth: 2,
strokeStyle: "#225588"
};
var sourceEndpointOptions = {
endpoint: "Dot",
paintStyle: { fillStyle: "#225588", radius: 7 },
endpoint: ["Dot", { cssClass: 'sourceEndpoint', radius: 5 }],
paintStyle: { fillStyle: '#225588' },
isSource: true,
isTarget: false,
connector: ["Flowchart"], // gap needs to be the same as makeTarget.paintStyle.radius
@@ -48,6 +48,9 @@
// deserialize the previously locally saved workflow
loadActivities(localId);
// create activity overlays for controlling their states
createOverlays();
// a new connection is created
jsPlumb.bind("jsPlumbConnection", function (connectionInfo) {
// ...update your data model here. The contents of the 'connectionInfo' are described below.
@@ -63,7 +66,7 @@
// instanciates a new workflow widget in the editor
var createActivity = function (activityName) {
renderActivity(null, activityName, {}, 10, 10);
renderActivity(null, activityName, {}, false, 10, 10);
};
@@ -74,7 +77,7 @@
createActivity(activityName);
});
var renderActivity = function (clientId, name, state, top, left) {
var renderActivity = function (clientId, name, state, start, top, left) {
$.ajax({
type: 'POST',
@@ -90,6 +93,10 @@
dom.addClass('activity');
if (start) {
dom.addClass('start');
}
if (clientId) {
dom.attr('id', clientId);
}
@@ -97,7 +104,7 @@
var editor = $('#activity-editor');
editor.append(dom);
jsPlumb.draggable(dom, { containment: "parent", scroll: true });
jsPlumb.draggable(dom, { containment: "parent", scroll: true, drag: function () { hideOverlay(true); } });
jsPlumb.makeTarget(dom, {
dropOptions: { hoverClass: "dragHover" },
@@ -110,6 +117,7 @@
elt.viewModel = {
name: name,
state: state,
start: start,
clientId: dom.attr("id"),
};
@@ -143,6 +151,78 @@
};
var onActivity = false;
var onOverlay = false;
$("#initial-state").button();
var createOverlays = function () {
var events = $('.activity.blocking');
$('#activity-overlay').hover(
function () {
onOverlay = true;
},
function () {
onOverlay = false;
setTimeout(hideOverlay, 50);
});
hideOverlay();
events.hover(
// mouse enter
function () {
var self = $(this);
var overlay = $('#activity-overlay');
overlay.position({
my: "right bottom",
at: "right top",
offset: "10 10",
of: self, // or $("#otherdiv)
collision: "none"
});
onActivity = true;
if (!overlay.is(":visible")) {
var state = $("#initial-state");
state.prop('checked', self.hasClass('start')).button("refresh");
state.unbind("click").click(function () {
updateStart(self, state.is(":checked"));
});
overlay.show();
}
},
// mouse leave
function () {
onActivity = false;
setTimeout(hideOverlay, 50);
}
);
};
var hideOverlay = function (force) {
if (force || (!onOverlay && !onActivity)) {
var overlay = $('#activity-overlay');
overlay.offset({ top: 0, left: 0 });
overlay.hide();
onOverlay = false;
onActivity = false;
}
};
var updateStart = function(dom, isStart) {
var clientId = $(dom).attr('id');
var activity = getActivity(localId, clientId);
$(dom).get(0).viewModel.start = isStart;
$(dom).toggleClass('start', isStart);
};

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using Orchard.Events;
using Orchard.Workflows.Models;
namespace Orchard.Workflows.Services {
public interface IWorkflowManager : IEventHandler {
/// <summary>
/// Triggers a specific Event, and provides the tokens context if the event is
/// actually executed
/// </summary>
/// <param name="name">The type of the event to trigger, e.g. Publish</param>
/// <param name="tokensContext">An object containing the tokens context</param>
void TriggerEvent(string name, Func<Dictionary<string, object>> tokensContext);
ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, Dictionary<string, object> tokens);
}
}

View File

@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Newtonsoft.Json;
using Orchard.Forms.Services;
using Orchard.Logging;
using Orchard.Workflows.Models;
using Orchard.Data;
using Orchard.Localization;
using Orchard.Tokens;
using Orchard.Workflows.Models.Descriptors;
namespace Orchard.Workflows.Services {
public class WorkflowManager : IWorkflowManager {
private readonly IActivitiesManager _activitiesManager;
private readonly IRepository<ActivityRecord> _activityRepository;
private readonly IRepository<WorkflowRecord> _workflowRepository;
private readonly IRepository<AwaitingActivityRecord> _awaitingActivityRepository;
private readonly IRepository<WorkflowDefinitionRecord> _workflowDefinitionRepository;
private readonly ITokenizer _tokenizer;
public WorkflowManager(
IActivitiesManager activitiesManager,
IRepository<ActivityRecord> activityRepository,
IRepository<WorkflowRecord> workflowRepository,
IRepository<AwaitingActivityRecord> awaitingActivityRepository,
IRepository<WorkflowDefinitionRecord> workflowDefinitionRepository,
ITokenizer tokenizer) {
_activitiesManager = activitiesManager;
_activityRepository = activityRepository;
_workflowRepository = workflowRepository;
_awaitingActivityRepository = awaitingActivityRepository;
_workflowDefinitionRepository = workflowDefinitionRepository;
_tokenizer = tokenizer;
Logger = NullLogger.Instance;
}
public Localizer T { get; set; }
public ILogger Logger { get; set; }
public void TriggerEvent(string name, Func<Dictionary<string, object>> tokensContext) {
var tokens = tokensContext();
var activity = _activitiesManager.GetActivityByName(name);
if (activity == null) {
Logger.Error("Activity {0} was not found", name);
return;
}
var startedWorkflows = new List<ActivityRecord>();
// look for workflow definitions with a corresponding starting activity,
startedWorkflows.AddRange(_activityRepository.Table.Where(
x =>x.Name == name && x.Start && x.WorkflowDefinitionRecord.Enabled
)
);
var awaitingActivities = new List<AwaitingActivityRecord>();
// and any running workflow paused on this kind of activity
awaitingActivities.AddRange(_awaitingActivityRepository.Table.Where(
x => x.ActivityRecord.Name == name && x.ActivityRecord.Start == false
).ToList()
);
// if no activity record is matching the event, do nothing
if (!startedWorkflows.Any() && !awaitingActivities.Any()) {
return;
}
// evaluate processing condition
awaitingActivities = awaitingActivities.Where(a => {
var formatted = JsonConvert.DeserializeXNode(a.ActivityRecord.State).ToString();
var tokenized = _tokenizer.Replace(formatted, tokens);
var serialized = JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized);
var context = new ActivityContext { Tokens = tokens, State = state };
// check the condition
try {
return activity.CanExecute(context);
}
catch (Exception e) {
Logger.Error("Error while evaluating an activity condition on {0}: {1}", name, e.ToString());
return false;
}
}).ToList();
// evaluate processing condition
startedWorkflows = startedWorkflows.Where(a => {
var formatted = JsonConvert.DeserializeXNode(a.State).ToString();
var tokenized = _tokenizer.Replace(formatted, tokens);
var serialized = JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized);
var context = new ActivityContext { Tokens = tokens, State = state };
// check the condition
try {
return activity.CanExecute(context);
}
catch (Exception e) {
Logger.Error("Error while evaluating an activity condition on {0}: {1}", name, e.ToString());
return false;
}
}).ToList();
// if no activity record is matching the event, do nothing
if (!startedWorkflows.Any() && !awaitingActivities.Any()) {
return;
}
// load workflow definitions too for eager loading
_workflowDefinitionRepository.Table
.Where(x => x.Enabled && x.ActivityRecords.Any(e => e.Name == name))
.ToList();
// resume halted workflows
foreach (var a in awaitingActivities) {
ResumeWorkflow(a, tokens);
}
// start new workflows
foreach (var a in startedWorkflows) {
StartWorkflow(a, tokens);
}
}
private void StartWorkflow(ActivityRecord activityRecord, Dictionary<string, object> tokens) {
var lastActivity = ExecuteWorkflow(activityRecord.WorkflowDefinitionRecord, activityRecord, tokens);
// is the workflow halted on a blocking activity ?
if (lastActivity == null) {
// no, nothing to do
}
else {
// workflow halted, create a workflow state
var workflow = new WorkflowRecord {
WorkflowDefinitionRecord = activityRecord.WorkflowDefinitionRecord
};
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
ActivityRecord = activityRecord,
WorkflowRecord = workflow
});
_workflowRepository.Create(workflow);
}
}
private void ResumeWorkflow(AwaitingActivityRecord awaitingActivityRecord, Dictionary<string, object> tokens) {
var lastActivity = ExecuteWorkflow(awaitingActivityRecord.WorkflowRecord.WorkflowDefinitionRecord, awaitingActivityRecord.ActivityRecord, tokens);
// is the workflow halted on a blocking activity ?
if (lastActivity == null) {
// no, delete the workflow
_workflowRepository.Delete(awaitingActivityRecord.WorkflowRecord);
}
else {
// workflow halted, save state
awaitingActivityRecord.ActivityRecord = lastActivity;
}
}
public ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, Dictionary<string, object> tokens) {
var firstPass = true;
while (true) {
// while there is an activity to process
var activity = _activitiesManager.GetActivityByName(activityRecord.Name);
if (!firstPass && activity.IsBlocking) {
return activityRecord;
}
else {
firstPass = false;
}
var state = FormParametersHelper.FromJsonString(activityRecord.State);
var activityContext = new ActivityContext {Tokens = tokens, State = state};
var outcome = activity.Execute(activityContext);
if (outcome != null) {
// look for next activity in the graph
var transition = workflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint);
if (transition == null) {
return null;
}
else {
activityRecord = transition.DestinationActivityRecord;
}
}
else {
return null;
}
}
}
}
}

View File

@@ -1,10 +1,10 @@
.activity {
/*.activity {
border: 1px solid #346789;
/*box-shadow: 2px 2px 19px #aaa;
-o-box-shadow: 2px 2px 19px #aaa;
-webkit-box-shadow: 2px 2px 19px #aaa;
-moz-box-shadow: 2px 2px 19px #aaa;
-moz-border-radius: 0.5em;*/
-moz-border-radius: 0.5em; /
border-radius: 0.5em;
opacity: 0.8;
filter: alpha(opacity=80);
@@ -28,17 +28,51 @@
-moz-box-shadow: 2px 2px 19px #444;
opacity: 0.6;
filter: alpha(opacity=60);
}*/
.activity {
border: 1px solid #999;
border-radius: 2px;
opacity: 0.8;
filter: alpha(opacity=80);
/*min-width: 5em;*/
text-align: center;
z-index: 20;
position: absolute;
background-color: white;
font-family: helvetica;
padding: 1em;
font-size: 0.9em;
}
.activity:hover, .dragHover {
box-shadow: 2px 2px 19px #aaa;
-o-box-shadow: 2px 2px 19px #aaa;
-webkit-box-shadow: 2px 2px 19px #aaa;
-moz-box-shadow: 2px 2px 19px #aaa;
/*opacity: 0.6;
filter: alpha(opacity=60);*/
}
.blocking {
background-image: url(images/blocking.png) ;
background-position: top right;
background-repeat: no-repeat;
}
.start {
background-color: #eee;
border-width: 2px;
}
._jsPlumb_connector { z-index:4; }
._jsPlumb_endpoint { z-index:21;cursor:pointer; }
.sourceEndpoint {
fill: red;
}
.sourceEndpointLabel, .targetEndpointLabel {
z-index: 21;
cursor: pointer;
@@ -65,12 +99,14 @@
.connection-label {
z-index: 10;
opacity:0.8;
border-radius: 2px;
filter:alpha(opacity=80);
background-color:white;
color:black;
padding:0 0.5em;
border:1px solid #346789;
border:1px solid #999;
opacity: 0.8;
filter: alpha(opacity=80);
}
/* toolbox */
@@ -96,3 +132,14 @@
font-size: 1.077em;
}
#activity-overlay {
position: absolute;
z-index: 30;
width: 100px;
height: 20px;
background-color: #EAEAEA;
border: 1px solid #ccc;
left: -2000px;
padding: 2px;
}

View File

@@ -0,0 +1,19 @@
.diamond {
width: 20px;
height: 20px;
margin-left: 20px;
border: 2px solid #333;
background-color: #999;
left: 50%;
margin-top: 10px;
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 0 100%;
-moz-transform-origin: 0 100%;
-ms-transform-origin: 0 100%;
-o-transform-origin: 0 100%;
transform-origin: 0 100%;
}

View File

@@ -6,6 +6,6 @@
string blockingClass = blocking ? "blocking" : null;
}
<div class="@blockingClass" style="background-color: pink">
<div class="@blockingClass">
@name.CamelFriendly()
</div>

View File

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

View File

@@ -13,6 +13,8 @@
<li class="activity-toolbox-item" data-activity-name="@activity.Name">
<h2>@activity.Name</h2>
</li>
Style.Include("workflows-activity-" + activity.Name.ToLower());
}
</ul>
</div>

View File

@@ -6,6 +6,7 @@
Layout.Title = @T("Edit Workflow");
Style.Include("admin-workflows.css");
Script.Require("jQueryUI");
Style.Require("jQueryUI_Orchard");
Script.Include("jquery.jsPlumb-1.3.16-all-min.js");
// var editorShape = ((IShapeFactory)New).Create(activity.Name + "_Editor");
@@ -21,9 +22,13 @@
@Html.ValidationSummary()
<div id="activity-editor">
<span id="activity-overlay">
<input type="checkbox" id="initial-state" /><label for="initial-state">@T("Start")</label>
</span>
</div>
using (Script.Head()) {
using (Script.Head()) {
<script type="text/javascript">
//<![CDATA[
var renderActivityUrl = '@Url.Action("RenderActivity", "Admin", new { area = "Orchard.Workflows" })';
@@ -42,7 +47,7 @@ using (Script.Head()) {
</text>
}
}
else if(!Model.IsLocal) {
else if (!Model.IsLocal) {
<text>
var state = @Html.Raw(Model.WorkflowDefinition.JsonData);
sessionStorage.setItem(localId, JSON.stringify(state));
@@ -57,7 +62,7 @@ using (Script.Head()) {
@Html.Hidden("localId", Model.LocalId)
@Html.Hidden("data", String.Empty)
using (Script.Foot()) {
using (Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
$("form").submit(function () {
@@ -68,7 +73,7 @@ using (Script.Foot()) {
});
//]]>
</script>
}
}
<fieldset>
<button class="primaryAction" type="submit" name="submit.Save" value="@T("Save")">@T("Save")</button>

View File

@@ -59,9 +59,9 @@
@Html.ActionLink(entry.Name, "Edit", new { id = entry.WokflowDefinitionId })
</td>
<td>
@*@Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.QueryId, returnurl = HttpContext.Current.Request.RawUrl })*@ |
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.WokflowDefinitionId}) |
@*@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.QueryId }, new { itemprop = "RemoveUrl UnsafeUrl" })*@
@* 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 }) |
@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.WokflowDefinitionId }, new { itemprop = "RemoveUrl UnsafeUrl" })
</td>
</tr>
index++;