- June
- 20
- 2007
Modifying the Workflow Association Data in SharePoint 2007 Pre-Initiation
Posted by undercoverlaptop
No Comments »
One of our customers wanted to use the out-of-the-box Approval workflow but wanted to pre-fill the Approvers field with users from the items metadata. In another words, each document in a document library can potentially have a difference approver so rather than have the Approvers field pre-filled from the initial Association data, they want it populated from the Approvers field on the initiated item.
Initially, I started out modifying the out-of-the-box Approval InfoPath form (Review-RoutingInit.xsn) and was able to successfully populate the Approvers field with hardcoded data but I then realized that I didn't know what Item the workflow had been started on, which meant I couldn't figure out who the Approver was for the specific item that the workflow had been started. So, I scrapped that idea and decided to create a new InfoPath Workflow initiation page (this is the page that loads the InfoPath Forms on initiation).
After using Lutz Roeder's . NET Reflector to analyze the IniWrkflIPPage.aspx page, I was able to determine that if I wanted to change the AssociationData before it was populated into the InfoPath Form Control, I needed to modify the property m_formData before the page finished its databinding.
protected override void OnLoad(EventArgs ea)
{
base.OnLoad(ea);
/* Some code commented out */
this.m_assocTemplate = SPWorkflowAssociationCollection.GetAssociationForListItemById(this.m_listItem, associationId);
this.m_formData = this.m_assocTemplate.AssociationData;
–> this.m_formData = “My modified form data”;
/* More code commented out */
this.DataBind(true);
}
I then went on to modify the form data using metadata from the item that initiated the workflow. Because I wanted to inherit my custom InfoPath initiation form from IniWrkflIPPage.aspx, I realized that I couldn't change the code in the OnLoad event, but instead needed to change it on the DataBind event.
protected override void DataBind(bool raiseOnDataBinding)
{
if (m_listItem["Approver"] != null)
{
// Load form data into an XmlDocument
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(this.m_formData);
// Defines the namespace manager to handle using the 'my' namespace
XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
manager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD");
// Gets a collection of all the Users specificed in the Approver field (Requires Approver field on document library)
SPFieldUserValueCollection users = new SPFieldUserValueCollection(m_listItem.Web, m_listItem["Approver"].ToString());
string personXml = string.Empty;
// Loops through each user and builds out a new Person element
foreach (SPFieldUserValue user in users)
{
personXml += "<my:Person><my:DisplayName>" + user.User.Name + "</my:DisplayName><my:AccountId>" + user.User.LoginName + "</my:AccountId><my:AccountType>User</my:AccountType></my:Person>";
}
// Selects the Reviewers (aka Approver) in the form data and sets the innerXml = to the collection of users we built
xmlDoc.SelectSingleNode("/my:myFields/my:Reviewers", manager).InnerXml = personXml;
// overwrites the default association data with our newly modified data
this.m_formData = xmlDoc.InnerXml;
}}
As it turned out, apparently some of the global variables in the IniWrkflIPPage.aspx don't “port” very well over to my custom page, so I ended up faking it a bit. There is probably a better way to do this but at this point I didn't care J
using System;
using System.Collections.Generic;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.Office.InfoPath.Server.Controls;
namespace TeamEli.Sharepoint.Workflow.ApprovalWorkflow
{
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust"), PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class ApprovalIniWrkflIP : Microsoft.Office.Workflow.IniWrkflIPPage
{
//// Fields
protected new string m_listItemName;
protected new string m_listItemUrl;
protected new string m_workflowName;
protected new XmlFormView XmlFormControl;
//// Methods
protected override void OnInit(EventArgs e)
{
base.XmlFormControl = this.XmlFormControl;
base.m_workflowName = this.m_workflowName;
base.m_listItemName = this.m_listItemName;
base.m_listItemUrl = this.m_listItemUrl;
base.OnInit(e);
}
protected override void OnLoad(EventArgs ea)
{
base.OnLoad(ea);
this.XmlFormControl = base.XmlFormControl;
this.m_listItemName = base.m_listItemName;
this.m_workflowName = base.m_workflowName;
}
protected override void DataBind(bool raiseOnDataBinding) { //// See above. }
}
}
With the code above precompiled into a DLL, all I needed to was modify two lines of my copied IniWrkflIPPage.aspx and that was to add my assembly and to change the page inherits to our new codebehind page.
<%@ Assembly Name="TeamEli.Sharepoint.Workflow.ApprovalWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<<PUBLICKEYTOKEN>>"%>
<%@ Page Language="C#" Inherits="TeamEli.Sharepoint.Workflow.ApprovalWorkflow.ApprovalIniWrkflIP" MasterPageFile="~/_layouts/application.master" EnableSessionState="true" AutoEventWireup="false" %>
All that was left was to create a new Feature that calls our new initiation page. And volia!
Hopefully some of this made sense. If not, let me know.
Eli