Вы находитесь на странице: 1из 11

Create Custom Approval Workflow with SharePoint

Designer
Introduction
My client had a need for a document approval workflow. Their needs were outside of the out of the box approval workflow
in SharePoint.
The first requirement was that the document requiring approval needed to be routed, in series, to as many as three different
managers. Any one of them can reject it at any time. If the request is rejected, the workflow stops and the request is not
forwarded to the remaining managers. The request is rejected as a whole.
Their second requirement was that this be developed in SharePoint Designer. They were not interested in a Visual Studio
solution. They do not have the internal resources to support it.
The last requirement was that the data collection needed to be done using an InfoPath form. This allows them to distribute
the form to employees and partners inside and outside of the organization.

SharePoint
A document library was created on the SharePoint site to store InfoPath form. The only modification made to the new list
was to add a new column called “Current Status.” This column will be updated by the workflow to track the approval
process.
To track the tasks associated with the workflow, a new task list was created on the SharePoint site.

HEY! When creating a workflow in SharePoint Designer, the workflow will attach itself to the first task list in the “Lists”

gallery. To ensure that the workflow was attached to the correct task list, I named it with “AAA” at the beginning of the

name. (This can be changed later with no impact to the workflow.)

InfoPath
Creating the InfoPath document was pretty straight forward. The form is used to request access to several different systems.
The systems belong to various divisions. Each division is responsible for approving access to their own systems. The form was
designed to collect basic user information as well as the specific system access being requested.
In order to keep track of the divisions that needed to be notified of the approval requests, three hidden checkbox fields
where created to hold a Boolean value indicating the division(s) that required approval for the request. To hide the
checkboxes, the height and width properties were set to “0 px.”
To populate these fields, code was added to the code behind to capture update events on specific fields on the form.
The fields that are used to indicate the access requirement were added to the form as checkboxes. The checkbox properties
where updated to reference 1/0 for the TRUE/FALSE value. This allowed each value to be utilized as an integer.
To determine if any checkboxes are selected, the values of each of the checkboxes are added together and the sum is tested
for a value greater than zero. If the sum is greater than zero the field corresponding to the selected processes is updated
with a “1” to indicate a TRUE value.
public void UpdateApprovalFlags(object sender, XmlEventArgs e)
{
try
{
int i = 0;
// Check the Division 1 for required approval.
i = (int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess1", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess2", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess3", NamespaceManager).ValueAsInt;
if (i > 0)
{
CreateNavigator().SelectSingleNode("/my:myFields/my:chkDivsion1ApprovalFlag", NamespaceManager).InnerXml = "1";
}
else
{
CreateNavigator().SelectSingleNode("/my:myFields/my: chkDivsion1ApprovalFlag ", NamespaceManager).InnerXml
= "0";
}
// Check the Division 2 for required approval.
i = 0;
i = (int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess4", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess5", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess6", NamespaceManager).ValueAsInt;
if (i > 0)
{
CreateNavigator().SelectSingleNode("/my:myFields/my:chkDivsion2ApprovalFlag", NamespaceManager).InnerXml = "1";
}
else
{
CreateNavigator().SelectSingleNode("/my:myFields/my:chkDivsion2ApprovalFlag ", NamespaceManager).InnerXml = "0";
}
// Check the Division 3 for required approval.
i = 0;
i = (int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess7", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess8", NamespaceManager).ValueAsInt +
(int)CreateNavigator().SelectSingleNode("/my:myFields/my:chkProcess9", NamespaceManager).ValueAsInt;
if (i > 0)
{
CreateNavigator().SelectSingleNode("/my:myFields/my:chkDivsion3ApprovalFlag", NamespaceManager).InnerXml = "1";
}
else
{
CreateNavigator().SelectSingleNode("/my:myFields/my:chkDivsion3ApprovalFlag", NamespaceManager).InnerXml = "0";
}
}
catch (Exception ex)
{
// Call to global error handler
}
}

The Change Event for each of the fields needs to perform the same function. To accommodate this, an event manger for
each field was added to the InternalStartup method of the InfoPath form. Each event manager was directed to
theUpdateApprovalFlags method.
public void InternalStartup
{
// Division 1
EventManager.XmlEvents["/my:myFields/my:chkProcess1"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess2"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess3"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
// Division 2
EventManager.XmlEvents["/my:myFields/my:chkProcess4"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess5"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess6"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
// Division 3
EventManager.XmlEvents["/my:myFields/my:chkProcess7"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess8"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
EventManager.XmlEvents["/my:myFields/my:chkProcess9"].Changed
+= new XmlChangedEventHandler(UpdateApprovalFlags);
}

To communicate the values of the checkbox fields, the fields were promoted as properties when the document was
published to SharePoint.
SharePoint Designer
A new work flow was designed in SharePoint Designer. The workflow was linked to the document library that was created to
store the InfoPath form. The only option selected << FIX THIS >>
Six workflow variables were created to:

 ID: This variable holds the ID of the task used to collect data from the approver.
 Approval: This variable holds the value of the “Approval” field in the task used to approve or reject the request.
 Comments: This variable holds the value of the “Comments” field in the task used to approve or reject the
request.
 Link: Document name with the hyperlink attached
 HelpDeskTicketID: The ID of the task created on the Help Desk task list
 HelpDeskTicketComment: A string value that contains comments plus the link from the "Link" variable
The initial step of the workflow sets the stage for the following steps:
 An email is sent to the requestor indicating that the request has been submitted.
 The Current Status field of the request item is updated to show that the workflow has been started and the
email sent to the requestor.
 An entry is made in the workflow log indicating that the workflow has started and the requestor has been
emailed.
 The "Link" workflow variable is populated with the dynamic string that creates an HREF tag with a link to the
InfoPath documents that was submitted to the document library.
Next, a new workflow step was created for each level of approval required.

The first approval step does not need to be as detailed as the subsequent steps. This step only needs to confirm the value of
the checkbox indicator for Division 1 approval.
If the checkbox value is TRUE, the workflow follows these actions in series:
 Update the item attached to the workflow.
 Update the workflow log.
 Create a new approval task to be completed by the approver. This is implemented as “Collect Data from a User.”
 After the approver completes the approval form, the workflow will collect the data from the form and store it in
the workflow variables defined earlier.
 If the first flag is not set, the “Approval” workflow variable is set to “Pending.” (This prevents any errors due to
null values when referencing the variable in the next step.)
When using the “Collect Data from a User” action the Custom Task Wizard is presented. It allows you to specify the data
that needs to be collected from the user. For this workflow only Approve/Reject and some comments are required.
Here are the steps required to complete the Custom Task Wizard:
 Provide a name and description for the custom data collection form.

 2. Add fields to the data form


 3. Define the fields
 Approval Field
 The first field is the Approval field. As subsequent steps also require custom tasks, each field is
named with a prefix that gives it a unique name.
 Choose the information type. For this item, the user will be required to make a choice, so
“Choice” is the obvious type.
 Next, define the choices. A default value can be defined, although in this case one is not
provided.
 Choose the Display type. Radio buttons ensure that the choices are mutually exclusive.
 The form requires a response for this field, so the “Allow blank values” checkbox was unchecked

 Comments Field
 The second field is the Comments field. As subsequent steps also require custom tasks, each field
is named with a prefix that gives it a unique name.
 Choose the information type. For this item, the user will be allowed to enter free text. As some
situations require a larger explanation, this field is set up as “Multiple Lines of Text."
 Next, update the field properties. A default value can be defined, although in this case one is not
provided.
 The field does not require a response for this field, so the “Allow blank values” checkbox was left
checked.
 The “Allow rich HTML” button was unchecked. This option places HTML on the completed form
regardless of the initial data entered into the field. The HTML tags actually show in the field of
the completed task.
 The “Append Text” checkbox was checked to allow users to update the comments field after the
task has been completed.

When the approver edits the task, the form looks like this:

HEY! When you create more than one data collection task in the workflow, SPD does not show the additional field

names when using the function builders. There may be an easier way to do it, but the method I used to resolve this is:

Click "Finish". The workflow should save, even if there are errors. Then re-open the workflow. The workflow task

fields should be available now.

The second approval step of the workflow is a bit more complex.


 The first condition tests the value of the Approval workflow variable for the value of “Reject”.
If the condition is true:
An email is sent to the requestor informing them that their request has been rejected
The workflow log is updated showing the rejection and the step it was rejected from
The workflow Current Status field of the workflow item is updated to show that the request is complete, but
rejected
The workflow is stopped.
 2. The second condition tests the value of the checkbox indicator for Division 2 approval. An addition condition is set to
re-test the value of the Approval workflow variable. (This is just a safety catch.)
If the condition is true:
An entry is made to the workflow log indicating that the approval task has been created
The Current Status field on the request item is updated with the new status
Data is collected from the approver
o This action is built the same way as the first approval step
The Approval workflow variable is populated with the data from the Approval form
The Comments workflow variable is populated with the data from the Approval form

The third approval step is identical to the second, with the exception of logging and email content specifics.
The first condition of the last approval step is used to validate the previous approval step by testing the Approvalworkflow
variable in the same way as the previous approval steps.
The second condition will only fire if the request has been approved by all appropriate divisions. These actions will create a
Help Desk ticket and a final workflow task.
An email is sent to the requestor indicating that the request has been approved. The email also states that a
final email will be sent when the request has been completed.
An entry to the workflow log is created showing the request approval.
A Dynamic String is created to build an entry for the Comments field of the new Help Desk Ticket. This string
utilizes the Link workflow variable created in the initial step of the workflow.

The Help Desk Ticket is created


An entry is created in the workflow log indicating that the Help Desk Ticket was created
An additional entry into the workflow log is created showing the ID of the Help Desk ticket
The Current Status field of the request item is updated indicating the new status
A data collection task is created for the help desk staff to complete when they have completed all necessary
processes to fulfill the request. This task is very similar to the previous data collection tasks with the exception
of a “Completed” field rather than an “Approval” field.

The last step of the workflow provides the final actions for the workflow:
 An entry is created for the workflow log indicating that the workflow is complete
 The request item is updated showing the final status of the workflow
 An email is sent to the requestor indicating that the request has been completed

Adding new approval steps can be done by adding a new step, moving it into position and duplicating the the second
approval step items.
Thanks for listening.

Вам также может понравиться