El Blanco's Office 2007 Blog

Wednesday, July 26, 2006

InfoPath 2007 Features

There is an excellent article on MSDN located here that describes the new features of InfoPath 2007 - well worth a read for those looking at getting started with the new InfoPath client.

Monday, July 17, 2006

Business Data Catalog Made Easy

Kudos to Todd Baginski for creating a useful tool to help create the awful XML file that's necessary in defining BDC applications.

Information on the tool, and a link to the download, can be found here so keep an eye open for updates.

Friday, July 14, 2006

Contact Selector ActiveX Control in Workflow Forms

If like me you were a little confused when you opened the sample InfoPath workflow forms from the ECM starter kit which include the Contact Selector ActiveX control, and you couldn't do anything with the control then don't worry . . .

You need to add the control to your toolbox before you can use it. To do this:
  • Open the form in question in InfoPath 2007 and in the Design Tasks pane, click "Controls"
  • On the Controls pane, click the "Add or Remove Custom Controls ..." link (near the bottom)
  • In the Add or Remove Custom Controls dialog box, click the Add button
  • In the "Select a Control Type" page, select "ActiveX Control" and click Nex
  • tFrom the large list of ActiveX controls, select "Contact Selector" and click Next
  • Select "Don't include in a .cab file" and click Next
  • For the "Binding property" select "value" and click Next
  • For Field or group type select "Field or group (any data type)" and click Finish
  • Click Close and then click OK.

The Contact Selector control will now be listed in the Custom section of the Controls pane and you should be able to access the properties of the control in the form.

Thursday, July 13, 2006

MOSS and RMS Woes

As a nice change from Workflow I thought I'd post about the recent experiences I've had trying to integrate MOSS with Windows Rights Management Server. I'm a big fan of RMS - protecting documents and emails provides fantastic security for organisations, so when I read about integrating RMS technology with MOSS I was quite excited. However the excitement didn't last long when I tried to get it working :)

Within my organisation we already have RMS up and running, so following the documentation from the ECM starter kit I figured it would be pretty straightforward to get a MOSS installation to work alongside - this wasn't the case !

Installing Windows RMS client SP2 onto the MOSS server was straightforward and got around the error seen in Central Administration when the client is not present. I then typed the URL to the RMS server in Central Administration and clicked OK and saw the error detailed in the setup guide - "The required windows rights management client is present but the server refused access. IRM will not work until the server grants permission". As detailed in the install guide, to get around this problem I modified the access control list on the following file on the RMS server:

c:\Inetpub\wwwroot\_wmcs\Certification\ServerCertification.asmx

but this didn't get around the issue, even when I added "<Domain Name>\Domain Computers" to the ACL (I even tried adding "Everyone" with full control, but no luck !!). I've tried following the setup guide again and again on various environments:
  • A VPC
  • Our corporate RMS and MOSS installations.
  • A fresh, standalone installation in a fresh domain with just 2 servers in the environment (1 DC and RMS, 1 MOSS server)
  • VMWare server environments similar to the above

but I've not succeeded with any of these as yet, altough I have seen the following errors in the application event log:

--------------------------------------
Event Type: ErrorEvent
Source: Windows SharePoint Services 3
Event Category: IRM
Event ID: 5132
Date: 6/30/2006
Time: 4:21:42 PM
User: N/A
Computer: RMSDC02
Description:
Information Rights Management (IRM): There was a problem while trying to obtain and activate a machine certificate.

In order to execute RMS transactions on a machine, that machine will need a unique certificate. This certificate is stored locally in the RMS lockbox.

Additional Data
Error value: 8004cf40

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
--------------------------------------

--------------------------------------
Event Type: ErrorEvent
Source: Windows SharePoint Services 3
Event Category: IRM
Event ID: 5053
Date: 6/30/2006
Time: 4:21:41 PM
User: N/A
Computer: RMSDC02
Description:
Information Rights Management (IRM): There was a problem while trying to acquire a machine certificate from the local Rights Management Services (RMS) lockbox.The specific problem could not be determined.

In order to execute RMS transactions on a machine, that machine will need a unique certificate. This certificate is stored locally in the RMS lockbox.

Additional DataError value: 8004cf40
For more information, see Help and Support Center at
http://go.microsoft.com/fwlink/events.asp.
--------------------------------------

I'll keep banging my head against the wall and post again if I get anywhere with it (I'd be interested to hear from anyone who has managed to get this working as I'm having my doubts about it :) !!).

Introduction to Workflow Technologies

I know I keep raving on about Workflows in MOSS, but it's my main focus at the minute and I promise I'll move onto other Office-related goodies at some point :) Until then, for those that would like a gentle introduction to Workflows in SharePoint, checkout the post on Mark Harrison's blog giving a link to a document titled "Understanding Workflows in Windows SharePoint Services and the 2007 Microsoft Office Sytem" - it's a decent read !

Wednesday, July 12, 2006

Migrating a WorkFlow from beta1 TR to beta2

Just another quickie for today. I recently ported an existing SharePoint workflow written using VS.NET in beta1 TR timescales to the new beta2 products and thought I'd post a few of the nasties that I found along the way regarding changes between the two - some people said beta1TR workflows wouldn't work in beta2, but with a little tweaking you CAN save yourself the hassle of having to re-write them !

  1. To programatically associate a workflow with a list you use the SPWorkflowAssociation class. This used to have a constructor that you used to associate a workflow with a list, but now you need to call the static method SPWorkflowAssociation.CreateListAssociation instead.
  2. The SPLongOperation class moved from the Microsoft.SharePoint.ApplicationPages namespace to the Microsoft.SharePoint namespace (we used this a lot to oversee long running operations). I couldn't find any reference in the documentation to this class and had to use Reflector to find it.
  3. Any internal and private members you had written in the workflow.cs file that are referenced in the activity properties in the workflow designer all have to be made public as they can't be referenced otherwise ! These were giving errors in the workflow designer (i.e. the red exclamation points) but making them public, and double clicking on the blue icon next to the properties in the designer to rebind them solved this.
  4. When defining the workflow.xml file you need to have the line:

    TaskListContentTypeId=
    "0x01080100C9C9515DE4E24001905074F980F93160"

    in the <workflow> tag. If not, when you click on a task that the workflow has created, you get the out-of-the-box task properties edit form instead of any custom InfoPath forms you've specified. This GUID refers to a content type defined in an out-of-the-box feature which the out-of-the-box workflows appear to reference in their workflow.xml files too. I'm not sure why it's needed, but it won't work without it - this one took me an age to find :)

That's about it - if anyone has any more problems they've encountered upgrading beta1 TR workflows to beta2 I'd be interested to hear from you . . .

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

Tuesday, July 11, 2006

InfoPath 2007 File Attachment Control and Browser-Enabled Forms

The InfoPath 2007 File Attachment control can be used to upload documents into an InfoPath form, and works with both InfoPath client and browser-based forms. Dragging a File Attachment control onto the design surface adds a new field into the data source of type "Picture or File Attachment (base64)".

This
link describes the technical details on how this field is formatted.

Basically, there is a header which is of length 24 bytes + length of the filename. Following this head is the file contents. These are base64 encoded.

To work with the uploaded file in .NET code in the InfoPath form you can use code similar to the following (which shows a button handler that copies the uploaded file to a local directory):

public void Button_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator docXN = this.CreateNavigator();
XPathNavigator opnXN = docXN.SelectSingleNode

("/my:myFields/my:FileAttachment",
this.NamespaceManager);
byte[] attachmentNodeBytes = Convert.FromBase64String
(opnXN.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 = attachmentNodeBytes[20] * 2;
byte[] fnBytes = new byte[fnLength];

// The actual filename starts at position 24 . . .

for (int i = 0; i <>
{
fnBytes[i] = attachmentNodeBytes[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[attachmentNodeBytes.Length - (24 + fnLength)];

for (int i = 0; i < fileContents.Length; ++i)
{
fileContents[i] = attachmentNodeBytes[24 + fnLength + i];
}

// We now have an array of bytes (fileContents) which we can
// do what we like with. I'll write them to a temporary file
FileStream fs = new FileStream(@"C:\Temp\InfoPath Test\" + fileName, FileMode.Create);
fs.Write(fileContents, 0, fileContents.Length); fs.Close();
}

Note that since this code is part of the InfoPath 2007 form the InfoPath form will need to have full trust (Tools -> Form Options -> Security and Trusts tab) and hence will need to be Admin deployed to the Microsoft Office SharePoint Server 2007 via Central Administration (don't forget to publish the form to a network location first !).

I'm going to try to incorporate this technique into an InfoPath form used in a custom developed workflow developed using the Sequential SharePoint Workflow template from the Enterprise Content Management Starter Kit, which can be downloaded here.