Introduction
DNN introduced the Workflow API in version 7.4 which allows
custom modules to leverage the core workflow processes in their custom
module. You can watch the overview of
the 7.4 features here. Big thanks to Francesco Rivola and Xepient
Solutions for their work in this feature.
I finished a code
project and training tutorial for DNNHero.com on this topic. I created a module project from the ChrisHammond DAL2 C# template using the sample code in the template and layered on a
complete workflow process. This project
is only available to Premium DNNHero
subscribers at the moment. If you’re
planning on dealing with this topic, I recommend reading further and
subscribing.
What is Workflow?
Workflow allows you to create a publishing flow that
provides an approval process between when a user creates the content and when
the content becomes live. DNN has built
a few different workflow processes for the HTML and File Manager modules. Now that release 7.4 is out, third-party module
developers can tap into the same workflow mechanism and definitions. Content workflow in a DNN module, when done
right, has the following attributes:
- Review & Approve Content
- Different workflow processes can be chosen or defined
- Notifications to appropriate users
- Versioning and Rollback capability
Setup work for Workflow
The first thing you need to know about using the
Workflow API is that it works inherently with Content Items. My suggestion is to use the Content Items API
as a storage mechanism for your versioned content (more on this later). To do this, you need to create a content item
type for your custom versioned content.
You also need to register your 5 workflow action classes as per Francesco’s
instructions in the introduction
video. To do this, I suggest implementing
IUpgradeable in your module in order to perform these one-time actions.
string IUpgradeable.UpgradeModule(string Version)
{
WorkflowActionManager _workflowActionManager = new WorkflowActionManager();
var message = String.Format("DotNetNuclear Workflow Upgradeable actions for version {0}.)", Version);
switch (Version)
{
case "00.00.01":
// Register content type id necessary for participating in the workflow api
int contentTypeId = Integration.Content.Instance.AddContentType(Common.Constants.CONTENTTYPENAME);
message += "Added content type for workflow. " + Environment.NewLine;
// Register 5 workflow actions: StartWorkflow, CompleteWorkflow, DiscardWorkflow, DiscardState, CompleteState
_workflowActionManager.RegisterWorkflowAction(new WorkflowAction
{
ContentTypeId = contentTypeId,
ActionType = WorkflowActionTypes.StartWorkflow.ToString(),
ActionSource = typeof(WorkflowStartAction).AssemblyQualifiedName
});
_workflowActionManager.RegisterWorkflowAction(new WorkflowAction
{
ContentTypeId = contentTypeId,
ActionType = WorkflowActionTypes.CompleteWorkflow.ToString(),
ActionSource = typeof(WorkflowCompleteAction).AssemblyQualifiedName
});
_workflowActionManager.RegisterWorkflowAction(new WorkflowAction
{
ContentTypeId = contentTypeId,
ActionType = WorkflowActionTypes.DiscardWorkflow.ToString(),
ActionSource = typeof(WorkflowDiscardAction).AssemblyQualifiedName
});
_workflowActionManager.RegisterWorkflowAction(new WorkflowAction
{
ContentTypeId = contentTypeId,
ActionType = WorkflowActionTypes.DiscardState.ToString(),
ActionSource = typeof(StateDiscardAction).AssemblyQualifiedName
});
_workflowActionManager.RegisterWorkflowAction(new WorkflowAction
{
ContentTypeId = contentTypeId,
ActionType = WorkflowActionTypes.CompleteState.ToString(),
ActionSource = typeof(StateCompleteAction).AssemblyQualifiedName
});
message += "Added workflow actions. " + Environment.NewLine;
break;
}
return message;
}
The other thing we need to setup is a module setting for allowing the administrator to select the workflow they want to implement. These workflows are defined in the DNN core. Here is a screenshot of the module setting I added.
To get the list of workflows defined in the current portal, I use the following
code:
WorkflowManager _workflowManager = new DotNetNuke.Entities.Content.Workflow. WorkflowManager();
var workflows = _workflowManager.GetWorkflows(PortalId).Select(w => new { WorkflowID = w.WorkflowID, WorkflowName = w.WorkflowName });
Versioning and Workflow
The basis for how I do versioning is as follows: I create my main “Item” which is my module’s domain object. This would be equivalent to an “Article” or “HTML” entity in other publishing modules. I use the DAL2 data layer to persist my object which is the same code created by the template. At the same time, I use the Content Items API in DNN to create my versioned items. The content field of the versioned item is a json-serialized version of my Item entity class. I also use the DAL2 Item ID (primary key) as the Content Key of the Content Item to relate back to the main Item. This approach has the advantage of not adding overhead to the “reads” of our main Item repository – we only need to read from the DAL2 controller to get the main items. Version items (content items) are only needed in the editing process.
Create New “Item”
When you save an item, this is the flow that should happen. You need to create the DAL2 item and then create the versioned content item with the Item ID as its Content Key. Then you will send that versioned content item into the workflow process.
WorkflowEngine _workflowEngine = new WorkflowEngine();
_workflowEngine.StartWorkflow(workflowId, contentItem.ContentItemId, PortalSettings.Current.UserId);
The workflow ID in the StartWorkflow parameters comes from the module setting the user has selected as the desired workflow. The content Item ID comes from your versioned content item.
Update an “Item”
Updating an item is not much different than the create process. You don’t need to do anything to the main item, just create the versioned item and send it to the workflow process
Workflow State Actions
So how do we control what happens when we start the workflow? Remember the IUpgradeable method? Those action state classes that we registered will get called by the framework when users interact with the workflow items from the Notifications area of the User Messaging page. Here is a sample flow:
Create new item and start workflow using Content Approval workflow.
- My WorkflowStartAction’s DoActionOnStateChanging() is fired (no action)
- My WorkflowStartAction’s DoActionOnStateChanged() is fired (no action)
- My WorkflowStartAction’s GetActionMessage() is fired (produce message content)
- A Notification is sent to the appropriate user (if Content Approval, then it is the content creator to Submit for review). The subject and body of this message is defined in the GetActionMessage() return.
- Creator user gets Notification message with action links for Submit and Discard. User clicks the Submit link.
- My StateCompleteAction’s DoActionOnStateChanging() is fired (no action)
- My StateCompleteAction’s DoActionOnStateChanged () is fired (no action)
- My StateCompleteAction’s GetActionMessage() is fired (produce message content)
- A Notification is sent to the appropriate users with the action links to Approve or Discard the content. The review clicks Approve link.
- My WorkflowCompleteAction’s DoActionOnStateChanging() is fired (no action)
- My WorkflowCompleteAction’s DoActionOnStateChanged () is fired. Here I have code that will take the content item from the workflow, deserialize it, update the main content item, and set the main content item to active.
Version History
The Workflow API doesn't help us with showing users the version history. We need to handle this. I created a simple module view that lays out all content items related to the main item and provides some viewing and workflow actions. In this screenshot you can see I added a Version History button to the main module view. When clicked it takes you to the History module view.
The version History view shows the versions associated with the main item. The bold indicates the versioned item that matches the main item (current). You can delete, view, and move the item to the next state, where applicable.
Next Steps
Get the
code and watch the full tutorial on DNNHero.com. If you are a subscribing member, you get
access to the Premium Forums, many other advanced module development tutorials
and code projects, and over a hundred hours of other administration videos and
how to’s.