-
Notifications
You must be signed in to change notification settings - Fork 26
Custom Workflows
custom workflows are scripted using javascript. as with all backoffice extensions, you start off in the puckweb/Areas/puck/Views/Shared/includes.cshtml
file.
here's the skeleton of a workflow:
<script>
var workflows = [];
workflows["Homepage"] = {
comment: function (workflowItem, userObject,startingState,currentState) {
console.log(workflowItem,userObject,startingState,currentState);
return "Please enter a comment";
},
handler: function (isPublished, workflowItem, userObject, startingState, currentState, commentObject, services) {
console.log(isPublished,workflowItem,userObject,startingState,currentState,commentObject,services);
services.add("needs-approval","newly created, please approve","Approver","");
return false;
}
};
</script>
so as you can see, you specify a workflows
array which you then add entries to, for each Type
of ViewModel you want workflows for. in the example above, we've added an entry for the Homepage
ViewModel and you can see that the workflow object contains two functions, comment
and handler
.
comment
decides whether or not a user needs to add a comment on saving/publishing a ViewModel. this comment is then passed onto the second function, the handler
. to decide whether or not a comment is needed, you are passed in the following parameters:
workflowItem:{
Id:int,
Status:string,
Message:string,
Group:string,
Complete:bool,
CompleteDate:datetime
Timestamp:datetime(created),
ContentId:string(guid),
Variant:string,
LockedBy:string(username),
LockedUntil:datetime
}
userObject:{
userName:string,
userRoles:array<string>,
userGroups:array<string>
}
as well as the above parameters workflowItem
and userObject
, the comment
function is also passed in the beginning state of the content being edited and the state at the point of saving in the form of FormData
objects. you can use the formdata.Get("NodeName")
method to get specific values from the content being edited. all of these parameters are passed in to the comment
function to help you decide whether or not you want to show a comment entry box after the user hits the save/publish buttons. if you don't want the user to enter a comment, return undefined from the comment
function, otherwise, return the string that will be used on the comment entry modal as the title.
the second function, handler
, is where you can add workflow entries. starting with the parameters - you have the current workflowItem
object, which has been detailed already and the userObject
, also detailed above. you also have isPublished
to let you know if a user is saving or publishing, startingState
which is a FormData
object with the state of the content being edited when first loaded and currentState
which is another FormData
object with the state of the content being edited at the point of saving. you also get passed in a user entered comment
if available (in a commentObject
that includes the comment string and any user mentions
) and a services object which will be detailed below:
services:{
add:function(status:string,message:string,userGroup:string,assignees:string(csv of usernames),
complete:function(),
msg:function(state:true(for positive messages - will display green)/false(for errors - will display red)/undefined(for neutral messages - will display grey),message:string)
}
as you can see above, the services
object has an add
function which allows you to add new workflow items, all arguments are required apart from assignees
, which is optional. you only need to use the complete
function (which has no arguments) on the last step of your workflow once everything is complete. moving from one step of your workflow to another you only need to use add
as this will automatically complete any previous step. returning true
from this function will cancel saving so in that event you may want to display a message and that is what the msg
function is for.
users will be notified in the backoffice when you add a workflow item belonging to a User Group
that they are in or assigned specifically to them using the assignees
argument.
how i see this being used is that you use all the information passed into the handler
to decide what step of the workflow you're moving to next. for example, with the information passed in, i can know that the current workflow step status is needs-approval
and that the current user editing the content is in the Approver
group and i can then complete
the workflow. or i can tell that the current step status is needs-approval
and the person currently trying to edit the content is not in the Approver
group and i can cancel the save event and show a user friendly message.
here's an example workflow:
<script>
var workflows = [];
workflows["Homepage"] = {
comment: function (workflowItem, userObject,startingState,currentState) {
return undefined;
},
handler: function (isPublished, workflowItem, userObject, startingState, currentState, commentObject, services) {
if (!workflowItem && userObject.userGroups.includes("Creator")) {
services.add("needs-approval", "newly created, please approve", "Approver", "");
} else if (workflowItem && workflowItem.Status == "needs-approval" && userObject.userGroups.includes("Approver")) {
services.add("approved-for-publishing", "this can now be published, after final checks", "Publisher", "");
} else if (workflowItem && !workflowItem.Complete && workflowItem.Status=="approved-for-publishing" && userObject.userGroups.includes("Publisher")) {
services.complete();
}
return false;
}
};
</script>
as you can see above, the comment
handler returns undefined
since we don't want any comments being entered. then, there's the first step where we check that the current workflowItem
is undefined
, meaning that this is likely newly created content and we create our first workflow item with a status of needs-approval
. we then move onto the approved state with a status of approved-for-publishing
and finally we complete
the workflow.
if a particular user is not allowed to modify the content in the current step of the workflow, perhaps because they are not in the correct User Group
, you can cancel the save event by returning true
and show a message:
<script>
var workflows = [];
workflows["Homepage"] = {
comment: function (workflowItem, userObject,startingState,currentState) {
return undefined;
},
handler: function (isPublished, workflowItem, userObject, startingState, currentState, commentObject, services) {
if (workflowItem && workflowItem.Status == "needs-approval" && userObject.userGroups.includes("Approver")) {
services.add("approved-for-publishing", "this can now be published, after final checks", "Publisher", "");
} else if (workflowItem && workflowItem.Status == "needs-approval" && !userObject.userGroups.includes("Approver")) {
services.msg(false,"you are not in the Approver group and cannot modify this content");
return true;
}
return false;
}
};
</script>
in the example above, if the current workflow step/status is needs-approval
but the user currently editing the content is not in the Approver
User Group
, you display an error using services.msg
and return true
to cancel the save event.
what if a user requires more than one save to complete a workflow? an example would be a copy editor who wants to see how the copy/text fits on a given page - they may edit a rich text editor field and save to do a preview multiple times.
in this instance, you may want to add a property
to your ViewModel
, a boolean
flag which indicates that they are finished editing, you can add the Property
to a specific tab on the edit screen by using the standard MVC Display
attribute and specifying the GroupName
option:
public class Homepage:BaseModel
{
[Display(GroupName = "Workflow")]
public bool ChecksComplete { get; set; }
}
you can then check the value of this field in the handler
function using the currentState
property and decide whether or not to move to the next step in your workflow.