Documente Academic
Documente Profesional
Documente Cultură
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
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.
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
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 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.