El Blanco's Office 2007 Blog

Wednesday, July 12, 2006

A WorkFlow that Uploads a Document via a Task using an InfoPath Form

OK, yesterday I posted how you could use the File Attachment control in InfoPath 2007 and programatically get at the uploaded file. This was a means to an end, and the main aim of the experiment was to incorporate just this sort of InfoPath form into a Microsoft Office SharePoint Server 2007 workflow, so that was my main task today. The objective was simple (or so it sounded) - develop a document-based review workflow that:

  • Displays an InfoPath initiation form where the workflow initiator can choose a user to review the document, and enter some comments.
  • Assign a task to the reviewer informing him/her that they have a job to do.
  • Displays an InfoPath task form so that when the reviewer clicks on the task they have been assigned an InfoPath form is displayed asking them to upload a review comments document.
  • Extracts the uploaded review comments document and uploads it to a SharePoint document library.
  • Completes the task, and sends an email informing the workflow initiator that the review is complete.

As easy as this sounds (I thought I'd done the hard bit working out the File Attachment control yesterday !) I did come accross some problems:

1. Creating a Task

I managed to get the OnWorkflowActivated activity working OK, so I then proceeded to add a CreateTask activity to the workflow - it turns out that the CreateTask activity needs it's own correlation token, even though the only one displayed in the design surface in VS.NET is the workflow's correlation token. To get around this I had to go into the Workflow1.designer.cs file and add my own additional System.Workflow.Runtime.CorrelationToken object that I could use for the task activities (CreateTask, OnTaskChanged, and CompleteTask).

2. Installing the Workflow

When I came to deploy the workflow feature to the server it turns out there's a couple of things you need to be aware of when developing your InfoPath forms.

a) I ran the stsadm -o installfeature to install the workflow and I received a "The XSN can not be used on server" error for one of my InfoPath forms (the one used when the reviewer clicks on the task).This turned out to be a security setting in the form in question. In InfoPath if you go to "Tools -> Form Options -> Security and Trust" you can set the security level of the form. There's a check box in there called "Automatically determine security level (recommended)" which was checked in my initiation form and this form worked fine (I initially copeid this form from the HelloWorldSequential workflow sampe and modified it for my needs).

Even though this was checkbox was ticked, you could see in the greyed-out options that the Initiation form had "Domain" level trust. The form I created from scratch form my task also had this checkbox ticked but the greyed-out options showed "Restricted" level. I un-ticked this checkbox and set the level to "Domain", re-published the task form and re-deployed the feature and this solved that problem.

b) The InfoPaths forms had submit buttons on them. In order to get these to function correctly you need to add a rule to the button with 2 actions:

  • submits the form to a data connection that is configured to submit to the hosting environment (note, you will have to add this data connection to the InfoPath form).
  • closes the form with no prompt.

When this was done the forms post back correctly as expected.

3. Running the Workflow

The workflow (eventually, after much debugging) ran OK and assigned the task to the reviewer. When the reviewer selected the task, the appropriate InfoPath form was dispayed asking the reviewer to upload the review document. Clicking on the File Attachment control in the InfoPath form, the reviewer could navigate to the review comments document on the local machine and click upload, but an error stating "The selected file was not found. Select another file" is displayed in a message box.

I haven't really found a suitable solution / explanation for this, but there is a workaround. You can edit the file "c:\program files\common files\microsoft shared\web server extensions\12\template\layouts\WrkTaskIP.aspx" and place the following javascript code into the file:

<script type="text/javascript">
aspnetForm.encoding = "multipart/form-data";
</script>

This must be placed inside the final <asp:content> control (the one called "PlaceHolderMain") as shown below:

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<script type="text\javascript">
aspnetForm.encoding = "multipart/form-data";
</script>
<SharePoint:FormComponent TemplateName="WorkflowEditFormToolBar" ControlMode="Edit" runat="server"/>
<InfoPath:XmlFormView id="XmlFormControl" runat="server" style="width:100%; height:100%;"/>
<SharePoint:FormDigest runat=server/>
</asp:Content>

This isn't really an ideal solution, as it involves modifying a standard MOSS page, but for now this is the only way I can find to get around this issue.

After all of this I now have the workflow up and running and it works as expected. I have yet implement the functionality to email the workflow initiator informing him / her that the review comments document has been uploaded, so I'll post again if I hit any problems with this but fingers crossed it should be OK !

If you're interested, the following is the code I used in a CodeActivity in the workflow to extract the document from the InfoPath form and upload it into SharePoint (you should notice a large similarity to the code I posted yesterday to extract the file from Infopath !):

private void uploadReviewCommnentsToSharePoint(object sender, EventArgs e)
{
byte[] attachmentBytes = Convert.FromBase64String(afterProps.ExtendedProperties["ReviewDoc"].ToString());

// Position 20 contains a DWORD indicating the length of the
// filename buffer. The filename is stored as Unicode so the
// length is multiplied by 2.
int fnLength = attachmentBytes[20] * 2;
byte[] fnBytes = new byte[fnLength];

// The actual filename starts at position 24 . . .
for (int i = 0; i < fnLength; ++i)
{
fnBytes[i] = attachmentBytes[24 + i];
}

// Convert the filename bytes to a string. The string
// terminates with \0 so the actual filename is the
// original filename minus the last character !
char[] charFileName = UnicodeEncoding.Unicode.GetChars(fnBytes);
string fileName = new string(charFileName);
fileName = fileName.Substring(0, fileName.Length - 1);

// The file is located after the header, which is 24 bytes long
//
plus the length of the filename.
byte[] fileContents = new byte[attachmentBytes.Length - (24 + fnLength)];
for (int i = 0; i < fileContents.Length; ++i)
{
fileContents[i] = attachmentBytes[24 + fnLength + i];
}

// Upload the file to the SharePoint document library where the
// workflow was initiated from . . .
SPFolder originalFolder = workflowProperties.List.RootFolder;
SPFile newFile = originalFolder.Files.Add(fileName, fileContents, true);
newFile.Item.Update();
if (newFile.CheckedOutBy != null)
{
newFile.CheckIn(String.Format("Review comments document for {0}", workflowProperties.Item.Name));
}


} // end of method

5 Comments:

  • Chris, I came across your post because of the mysterious "The XSN can not be used on server" error. Although in my case, I had to set it to "Full trust" mode.

    However, I'm wondering if you've come across another error when you try to activate the feature:

    "Failed to compare two elements in the array".

    This seems more like a .Net code level error and indeed, most searches on this phrase refernce Sort() on Array. Did you come across this error? And any idea what may be causing it?

    If you have any ideas, I'd appreciate it: cchen[at]charliedigital.com thanks.

    By Anonymous Anonymous, at 12:14 am  

  • Hi,

    Thanks for your method to solve the problem of uploading file in workflow task. It's worked. Great.

    By Anonymous Anonymous, at 11:40 am  

  • Hey Chris -

    Is this solution available for d/l? It's awsome and would be a great teaching tool.

    c

    By Anonymous Anonymous, at 3:59 pm  

  • Hello,

    Thanks for the tip on the file attachment! I have encountered another issue and was wondering if you've come across it.

    I have 2 views for 1 browser-enabled infopath form. I have the file attachment control in the default view and when the user closes the form, I want to display the read-only view of the form with the attachment that was provided. Do you know how to set the default value of the file attachment control?

    By Blogger spar, at 9:26 pm  

  • Hello,

    Thanks for the tip on the file attachment! I have encountered another issue and was wondering if you've come across it.

    I have 2 views for 1 browser-enabled infopath form. I have the file attachment control in the default view and when the user closes the form, I want to display the read-only view of the form with the attachment that was provided. Do you know how to set the default value of the file attachment control?

    By Blogger spar, at 9:26 pm  

Post a Comment

<< Home