Creating a Custom User Site Provisioning Solution with SharePoint Server 2007 (Part 2 of 2)

Summary: Examine new capabilities built in to the Microsoft Office SharePoint Server 2007 Enterprise Edition, which enable business process automation and simplify the business process. This article is part 2 of 2. (22 printed pages)

Sonya Zidek, Microsoft Corporation

August 2007

Applies to: 2007 Microsoft Office System, Microsoft Office SharePoint Server 2007, Windows SharePoint Services 3.0

Download the accompanying code sample, MOSS2007CustomSiteProvisioning.exe

Read Part 1: Creating a Custom User Site Provisioning Solution with SharePoint Server 2007 (Part 1 of 2)

Contents

  • Custom Permission Request Workflow

  • UserAccessProvisioning Tool

  • UserRequestUpload Tool

  • Conclusion

  • Additional Resources

Custom Permission Request Workflow

One of the most innovative features of the new MOSS 2007 Enterprise Edition is the workflow feature. The new workflow capabilities are extensive, including built-in support for the following types of workflow:

  • Approval

  • Collect Feedback

  • Collect Signatures

  • Disposition Approval

  • Group Approval

  • Translation Management

  • Issue Tracking

As a site administrator, you can instantiate these built-in workflows on any entity within MOSS with a few simple configuration steps.

However, for the permission request solution, a custom workflow is required to emulate properly the manual business process that the workflow is replacing. The workflow must accommodate sending various e-mail messages throughout the process, and depending on whether the request is approved, generate XML to be processed to add the requestor to the Members group within MOSS.

MOSS also provides a mechanism to create custom workflows. There are two primary developer tools to use for this: Microsoft Office SharePoint Designer 2007 and Microsoft Visual Studio 2005 Extensions for Microsoft Windows Workflow Foundation. Using SharePoint Designer allows a business analyst or a developer to use the workflow activities that come with Windows Workflow Foundation and MOSS. The built-in workflow actions that you can use by default in SharePoint Designer include the following:

  • Setting list metadata

  • Creating, copying, deleting, or changing list items (including documents)

  • Checking items in or out

  • Pausing, starting, or stopping the workflow

  • Sending e-mail messages

  • Setting the moderation status of an item

  • Setting an item's metadata

  • Assigning tasks

You can create custom workflows with SharePoint Designer by selecting an existing document library or list within an existing MOSS or Windows SharePoint Services 3.0 site, and then on the File menu, point to New and then click Workflow. The workflow designer wizard appears, allowing you to create a custom workflow without writing a single line of code. The workflow designer allows you to add conditions and actions to the workflow to determine when to execute specific actions.

For example, using the built-in actions mentioned earlier, you could develop a custom workflow that checks a metadata column of a particular document in a document library, and based on the value send an e-mail message or assign a task to the appropriate team member. In any case, SharePoint Designer allows for great flexibility in designing custom workflows without the need to write code.

Using Visual Studio 2005 with the Windows Workflow Foundation add-in allows you to create custom workflow actions and complex custom workflows. The add-in for Visual Studio imports Windows Workflow Foundation activities that are available by default for you to use in designing workflows. These activities are included in a Workflow toolbox and you can drag them to the design surface of the custom workflow. The next section examines this in more detail and describes the development of the custom workflow activity used in the permission request solution.

Figures 13a and 13b show the built-in workflow activities that are included with the Windows Workflow Foundation add-in for Visual Studio in the Workflow toolbox. Figure 13a shows the built-in activities for a custom state-machine workflow project.

Figure 13a. State-machine built-in workflow activity

State Machine workflow activity

Figure 13b shows the built-in activities for a custom sequential workflow project. For more information, see the workflow resources in Additional Resources.

Figure 13b. Sequential Workflow built-in workflow activity in Visual Studio 2005 toolbox

Sequential Workflow workflow activity

Although most of the actions that must be included in this permission request workflow are available as standard workflow activities with Windows Workflow Foundation and MOSS, a custom activity was developed to generate the XML file from the submitted request, which could then be processed on other servers within the SharePoint farm.

The custom workflow activity was developed with Visual Studio 2005 and Windows Workflow Foundation Extensions add-in. The activity was then deployed to the MOSS environment and used within a custom workflow. The custom workflow was developed by using SharePoint Designer.

Custom Workflow Activity

The Visual Studio 2005 Extensions for Windows Workflow Foundation provides simple templates for workflow development in Visual Studio 2005. The templates include the basic components needed to create the custom workflow activity.

For the permission request solution, a Sequential Workflow Library is created. This activity includes one action, which is the Execute Code action. Figure 14 shows the designer view for the UserRequestXML workflow activity designer view.

Figure 14. Custom UserRequestXML workflow activity designer view

UserRequestXML workflow activity designer view

The UserRequestXML class includes basic workflow components that are essential to a workflow activity. The first type of component is the DependencyProperty component. DependencyProperty indicates which initialization values you must supply to process the workflow activity. For the permission request solution, there are nine DependencyProperty properties. These properties pass in the employee request information submitted in the request form to generate the XML for processing the Member permissions. The following code example shows only one DependencyProperty declaration; however, you can find the complete set of code with the code sample that accompanies this article.

//User NTID Property
public static DependencyProperty UserNTIDProperty = 
System.Workflow.ComponentModel.DependencyProperty.Register("UserNTID", 
typeof(string), typeof(UserRequestXML));

[ValidationOption(ValidationOption.Required)]
[Description("Enter a variable to store the user's NT ID.")]
[Category("InputValues")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string UserNTID
{
get
{
return ((string)(base.GetValue(UserRequestXML.UserNTIDProperty)));
}

set
{
base.SetValue(UserRequestXML.UserNTIDProperty, value);
}
}

Notice that the UserNTID property that is used as the public property to access the DependencyProperty property UserNTIDProperty has several attributes defined: ValidationOption, Description, Category, Browsable, and DesignerSerializationVisibility. ValidationOption determines whether this DependencyProperty property is a required or optional property. In this case, the attribute is set to Required, which indicates that when this activity is used within a larger workflow, you must define a UserNTID property before processing the activity. Description serves as a developer-friendly description of the type of input that you must provide for this parameter. Category, set to InputValues in the permission request solution, indicates that this is an input type property and an output type property. If an output type property is needed, the Category attribute is set to ReturnValues. The Browsable property indicates whether to display this property in a Properties window (typically, in Visual Studio). Finally, the DesignerSerializationVisibility attribute indicates whether the value for a property is Visible and should persist, Hidden and should not persist, or consists of Content, which should have initialization code generated for each public (not hidden) property of the object assigned to the property.

The actual action of the UserRequestXML workflow activity is within the GenerateXML() method, which is the event handler for the Execute Code action within the workflow activity. This method is straightforward because in the case of the permission request solution it generates a simple XML document based on the request information supplied by the employee in his or her request.

public void GenerateXML(object sender, EventArgs e)
{
// Split the UserNTID field from the form information into a domain name 
// and user id.
int lengthToSlash = UserNTID.IndexOf("\\");
int lengthOfLogin = UserNTID.Length - 1;

string UserDomain = UserNTID.Substring(0, lengthToSlash);
string UserID = UserNTID.Substring(lengthToSlash + 1, lengthOfLogin – 
lengthToSlash);

string dateString = DateTime.Now.Date.ToShortDateString().Replace("/", "-
").ToString() + "_" + DateTime.Now.ToLongTimeString().Replace(":", "-
").ToString().Replace(" ","-").ToString();

// Creating XMLDocument with Site Usage Data.
XmlDocument xmlDoc = new XmlDocument();

// Write down the XML declaration.
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", 
null);

// Create the root element.
XmlElement rootNode = xmlDoc.CreateElement("UserAccessRequest");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);

XmlElement parentNode = xmlDoc.CreateElement("UserRequest");

xmlDoc.DocumentElement.PrependChild(parentNode);

// Create the required nodes.
XmlElement UserDomainNode = xmlDoc.CreateElement("UserDomain");
XmlElement UserIDNode = xmlDoc.CreateElement("UserID");
XmlElement UserFullNameNode = xmlDoc.CreateElement("UserFullName");
XmlElement UserEmailNode = xmlDoc.CreateElement("UserEmail");
XmlElement UserCostCenterNode = xmlDoc.CreateElement("UserCostCenter");
XmlElement ApproverNameNode = xmlDoc.CreateElement("ApproverName");
XmlElement ApproverEmailNode = xmlDoc.CreateElement("ApproverEmail");
XmlElement PersonalSiteNode = xmlDoc.CreateElement("PersonalSite");
XmlElement TechnicalSiteNode = xmlDoc.CreateElement("TechnicalSite");
XmlElement BusinessSiteNode = xmlDoc.CreateElement("BusinessSite");

// Retrieve the text. 
XmlText UserDomainText = xmlDoc.CreateTextNode(UserDomain);
XmlText UserIDText = xmlDoc.CreateTextNode(UserID);
XmlText UserFullNameText = xmlDoc.CreateTextNode(UserFullName);
XmlText UserEmailText = xmlDoc.CreateTextNode(UserEmail);
XmlText UserCostCenterText = xmlDoc.CreateTextNode(UserCostCenter);
XmlText ApproverNameText = xmlDoc.CreateTextNode(ApproverFullName);
XmlText ApproverEmailText = xmlDoc.CreateTextNode(ApproverEmail);
XmlText PersonalSiteText = xmlDoc.CreateTextNode(PersonalSite.ToString());
XmlText TechnicalSiteText = xmlDoc.CreateTextNode(TechnicalSite.ToString());
XmlText BusinessSiteText = xmlDoc.CreateTextNode(BusinessSite.ToString());

// Append the nodes to the parentNode without the value.
parentNode.AppendChild(UserDomainNode);
parentNode.AppendChild(UserIDNode);
parentNode.AppendChild(UserFullNameNode);
parentNode.AppendChild(UserEmailNode);
parentNode.AppendChild(UserCostCenterNode);
parentNode.AppendChild(ApproverNameNode);
parentNode.AppendChild(ApproverEmailNode);
parentNode.AppendChild(PersonalSiteNode);
parentNode.AppendChild(TechnicalSiteNode);
parentNode.AppendChild(BusinessSiteNode);

// Save the value of the fields into the nodes.
UserDomainNode.AppendChild(UserDomainText);
UserIDNode.AppendChild(UserIDText);
UserFullNameNode.AppendChild(UserFullNameText);
UserEmailNode.AppendChild(UserEmailText);
UserCostCenterNode.AppendChild(UserCostCenterText);
ApproverNameNode.AppendChild(ApproverNameText);
ApproverEmailNode.AppendChild(ApproverEmailText);
PersonalSiteNode.AppendChild(PersonalSiteText);
TechnicalSiteNode.AppendChild(TechnicalSiteText);
BusinessSiteNode.AppendChild(BusinessSiteText);

// Save to the XML file.
xmlDoc.Save("E:\\UserRequests\\UserAccessRequest_" + UserDomain + "-" + UserID 
+ "_" + dateString + ".xml");
}

Summary of Key Sample Files

The UserRequestXML subfolder in the SampleFiles directory contains all of the code for the custom workflow activity used in the permission request solution. The solution and project files are also included in the UserRequestXML folder. To view the code, open the UserRequestXML.sln file from Visual Studio 2005. Before attempting to open this solution, install the Visual Studio Extensions for Windows Workflow Foundation.

Custom Workflow Activity Deployment

The deployment process for a custom workflow activity is similar to that of any custom application based on the Microsoft .NET Framework. The workflow activity assembly must be signed, and then deployed to the global assembly cache of the server on which it is to be used. For the permission request solution, you must deploy the assembly to the global assembly cache of the server running MOSS.

Then, you must edit the web.config file of the MOSS server to include the appropriate tags to give permission for the activity assembly to execute on the MOSS SharePoint site. The following tag is added to the <authorizedTypes> section of the web.config file.

Note

Line breaks have been added to this code sample to facilitate online viewing. You will want to remove them before copying the code example.

<authorizedType Assembly="UserRequestXML, Version=1.0.0.0, Culture=neutral, 
PublicKeyToken=421d6b13b19171e5" Namespace="CustomerWorkflowUtilities.UserRequestXML" 
TypeName="*" Authorized="True" />

Finally, you must edit the WSS.ACTIONS file on the MOSS server. This is the most intricate part of the deployment process, because the definition of the activity within the WSS.ACTIONS file must accommodate each DependencyProperty property defined in the activity exactly, or the activity will not work within SharePoint Designer and the error is not evident. Therefore, the code that you add to the WSS.ACTIONS file for the permission request solution is discussed in detail.

You should add the following code example to the <Actions> section of the WSS.ACTION file. You can find this file in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\Workflow directory on the MOSS server.

<Action Name="Generate XML from User Request"
ClassName="CustomWorkflowUtilities.UserRequestXML"
Assembly="UserRequestXML, Version=1.0.0.0, Culture=neutral, PublicKeyToken=421d6b13b19171e5"
AppliesTo="all"
Category="Extras">
<RuleDesigner Sentence="Generate XML for User: %1, FullName %2, Email 
%3, CostCenter %4; Approver Full Name: %5, Approver Email: %6; Personal 
Site: %7, Technical Site: %8, Business Site: %9 ">
<FieldBind Field="UserNTID" TypeFrom="FieldName" Text="UserNTID" 
Id="1"/>
<FieldBind Field="UserFullName" TypeFrom="FieldName" 
Text="UserFullName" Id="2"/>
<FieldBind Field="UserEmail" TypeFrom="FieldName" Text="UserEmail" 
Id="3"/>
<FieldBind Field="UserCostCenter" TypeFrom="FieldName" 
Text="UserCostCenter" Id="4"/>
<FieldBind Field="ApproverFullName" TypeFrom="FieldName" 
Text="ApproverFullName" Id="5"/>
<FieldBind Field="ApproverEmail" TypeFrom="FieldName" 
Text="ApproverEmail" Id="6"/>
<FieldBind Field="PersonalSite" TypeFrom="FieldName" 
Text="PersonalSite" Id="7"/>
<FieldBind Field="TechnicalSite" TypeFrom="FieldName" 
Text="TechnicalSite" Id="8"/>
<FieldBind Field="BusinessSite" TypeFrom="FieldName" 
Text="BusinessSite" Id="9"/>
</RuleDesigner>
<Parameters>
<Parameter Name="UserNTID" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="UserFullName" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="UserEmail" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="UserCostCenter" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="ApproverFullName" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="ApproverEmail" Type="System.String, mscorlib" 
Direction="In" />
<Parameter Name="PersonalSite" Type="System.Boolean, mscorlib" 
Direction="In" />
<Parameter Name="TechnicalSite" Type="System.Boolean, mscorlib" 
Direction="In" />
<Parameter Name="BusinessSite" Type="System.Boolean, mscorlib" 
Direction="In" />
</Parameters>
</Action>

The main tag, <Action>, defines the action as a new possible action to use within SharePoint Designer. The Name attribute of the Action is how the Action is displayed within the workflow designer in SharePoint Designer. The ClassName attribute of the Action is the name of the assembly deployed in the global assembly cache of the MOSS server. The Category attribute of the Action determines how to categorize the action within the list of available actions in the workflow designer in SharePoint Designer.

The next sub element in the Action tag is the <RuleDesigner> tag. This defines all of the fields to expose within the workflow designer in SharePoint Designer that allow anyone to use the action. You define these fields by accessing the workflow properties and other lookup values available through the designer. The Sentence attribute of the RuleDesigner element specifies how to express the action within the workflow designer, and the order of the parameters that must be provided into the action. You can describe each of the parameters in a FieldBind child element and specify an ID to use within the Sentence attribute to display the actual value of the parameter.

Each of the FieldBind sub elements defined matches up to each DependencyProperty property that you defined within the custom workflow activity code. The fields defined in the <RuleDesigner> tag then correspond to each parameter defined in the Parameters child element. These Parameters child elements also correspond to the properties in the workflow activity. Within each Parameter tag, you define the name of the parameter by the Name attribute. Define the type of parameter to pass in by using the Type attribute. Finally, the Direction attribute helps define whether the parameter is an input or output parameter.

After you define and properly correlate these components of the <Action> tag to each DependencyProperty defined in the activity code, you should save the WSS.ACTIONS file. At this point, if everything is defined properly, the Action should appear in SharePoint Designer. When a new workflow is created and actions are added, the custom action appears in the menu under the specified Category.

Figure 15 shows an example of a workflow designer window open in SharePoint Designer. The Actions menu is expanded to expose the last ten actions used. You can use the More Actions command to view more actions.

Figure 15. A SharePoint Designer workflow design window

SharePoint Designer workflow design window

Figure 16 shows the options that appear when you click the More Actions command.

Figure 16. All actions that are available in SharePoint Designer

Available actions to use in SharePoint Designer

Custom Workflow with SharePoint Designer

The development of a custom workflow action demonstrates the flexibility and extensibility of the Windows Workflow Foundation, while the ability to integrate the custom activity into a custom workflow designed in SharePoint Designer demonstrates the integration capabilities with the 2007 Office system. The simplicity of the process to integrate this custom activity into SharePoint Designer also demonstrates the ease of use with both the Windows Workflow Foundation and SharePoint Designer.

The ability to create custom workflows in SharePoint Designer allows you to make business process additions to the SharePoint Products and Technologies system. The simplicity of creating a workflow demonstrates the vast possibilities for the business users within the enterprise.

To create an additional workflow, open the specific site that should include the workflow in SharePoint Designer, by starting SharePoint Designer and then clicking Open Site on the File menu. In the Open Site dialog box, type the URL of the SharePoint site to open. Figure 17 shows opening a site in SharePoint Designer.

Figure 17. Open Site dialog box in SharePoint Designer

Open Site dialog box in SharePoint Designer

Next, on the File menu, point to New and then click Workflow to open the workflow designer window and create a custom workflow. The workflow designer window opens with the initial workflow properties that you must specify to begin designing the workflow. Figure 18 shows the initial workflow designer window that opens up and the options that you must specify.

Figure 18. New Workflow Designer window

New Workflow Designer window

You must define any workflow designed in SharePoint Designer for a specific list on the SharePoint site. Unlike workflows developed in Visual Studio 2005, the workflow developed in SharePoint Designer cannot exist on its own without being associated with an existing list (or document library) on an existing SharePoint site.

You must define several workflow properties before you can actually design the workflow. The first property is the name of the workflow, which you add in the first box in the workflow designer window. The next property is the list to associate with this workflow, which you select from a drop down list of all of the existing lists and document libraries on the site to which SharePoint Designer is connected. Next are three check box options to determine how to initiate the workflow:

  • Allow this workflow to be manually started from an item

  • Automatically start this workflow when a new item is created

  • Automatically start this workflow whenever an item is changed

You can select these three initiation options depending on how to initialize the business process workflow. In addition to the standard initiation options that are offered, you can also add initiation parameters to the workflow by clicking the Initiation button at the bottom of the workflow designer window. Figure 19 shows the window that allows you to add initiation variables that are required to start processing the workflow. If the initiation is set to start automatically, you must apply default values within this initiation parameters window. Otherwise, users are prompted when they manually start the workflow.

Figure 19. Initiation Parameters window

Initiation Parameters window

Finally, after you define all of the initial properties, you can design the actual workflow steps. After entering the appropriate values for the properties, you can click the Next button to start developing the workflow actions. For the permission request solution, the workflow was named PermissionRequestWorkflow and associated with the UserSiteRequest forms library. Because this is the forms library that contains submitted new employee requests, the processing of the workflow is set to automatically start when a new form is submitted to the forms library.

Next, you must define the steps of the workflow. The workflow designer window provides an intuitive interface that allows the definition of steps in the workflow and appropriate conditions and actions to execute within the particular steps of the workflow. As with any other standard programming practice, the evaluation of the Conditions of the workflow step determine whether the Actions will execute. However, if a Condition is not defined, the Actions are executed. Figure 20 shows the standard designer window for specifying workflow steps, as well as Conditions, and Actions for the custom workflow.

Figure 20. Workflow Steps Designer window

Workflow steps designer window

For the permission request solution, the business process that is emulated within the workflow requires several other components to be in place for task assignment and workflow history tracking. Therefore, the task of reviewing the employee request is assigned to the cost center owner, proper notifications are sent to the appropriate recipients throughout the workflow process, and the history of each workflow step is tracked in a history list within the SharePoint site.

The first step within the permission request custom workflow is named CreateApprovalTask. Because this first step should execute when a new request is submitted, there is no condition defined for this step. The tasks for this workflow are stored in a new custom Tasks list named UserRequestWorkflowTasks, and the history list for the workflow is stored in a custom list, named UserRequestWorkflowHistoryList.

Figure 21. Permission Request Workflow Step 1 - CreateApprovalTask

Step 1 - CreateApprovalTask

The first step of the workflow includes the follow actions:

  • Create item in UserRequestWorkflowTasks  Creates a task in the UserRequestWorkflowTasks list, assigns it to the cost center approver, and outputs the new Task information, including the Task ID in the UserRequestWorkflowTasks list.

  • Then Set TaskID to UserRequestWorkflowTasks:ID  Sets the value of the newly created task ID to the TaskID column in the forms library to associate the submitted request form with the approval task for future update.

  • Then Create item in UserRequestWorkflowHistoryList  Creates a history item in the UserRequestWorkflowHistoryList that records the creation of the approval task.

  • Then Email UserSiteRequest:ApproverEmail  Sends an e-mail message to the cost center approver notifying him or her that a new task has been created for them, including the information about who submitted the request, and a link to the request for easy access.

  • Then Create item in UserRequestWorkflowHistoryList  Creates a history item in the UserRequestWorkflowHistoryList that records the sending of the e-mail notification to the cost center approver.

  • **The wait for Approval Status to not equal 2;#Pending  **Sets the workflow to a wait state until the cost center approver reviews the request and sets the Approval Status of the submitted request to either Approved or Rejected. The Approval Status is automatically placed into the Pending state when the approval workflow is initiated, and therefore the wait condition must be neutral and indicate a wait until the Approval Status is something other than Pending.

The second step in the permission request workflow is called ReactToRequestReview. This step requires that conditions are in place to determine whether the request was approved or rejected. Depending on the cost center approver’s assessment, the workflow reacts to the approval or rejection of the request. If the request was approved, the workflow then records the approval, sets the task to complete, records the sent notification, generates the necessary XML to add the requestor to the appropriate Members group within MOSS, and finally enters a history item to note the completion of the workflow. If the request is rejected, the rejection is noted in the history list, the approval task is set to complete, an e-mail notification is sent to the requestor, and finally a history item is entered to note the completion of the workflow.

Figure 22. Permission Request Workflow Step 2 - ReactToRequestReview

Step 2 - ReactToRequestReview

For the permission request workflow, if the request is approved, the request must be rolled into an XML file to add requestor to the correct Member group on the SharePoint server, which is the desired outcome of this business process. You accomplish this with the custom GenerateXML workflow activity, as discussed in the previous section. The GenerateXML action is shown earlier in the second step of the workflow process.

To add the GenerateXML action to the workflow, it must be selected from the Actions menu in the workflow designer. Because the GenerateXML action was deployed into the Extras category of workflow actions, it is located under the Extras category in the Workflow Actions dialog box when choosing which new action to add to the workflow. Figure 23 shows adding the GenerateXML action from the Workflow Actions dialog box.

Figure 23. GenerateXML custom activity available for use within SharePoint Designer

GenerateXML activity for SharePoint Designer

After the action is selected to use, it is added to the active workflow step in the workflow designer window. The properties of the GenerateXML custom activity appear in the workflow designer. For any action that requires you to define parameters prior to continuing processing, the parameters appear as underlined hyperlinks. Depending on the type of parameter that you must define, when the parameter item is clicked, various options appear to select the value to assign to the parameter. For the GenerateXML action, you must submit all of the employee request information to this action to generate the XML file. Figure 24 shows how the GenerateXML action appears in the workflow designer prior to assigning values to the required parameters.

Figure 24. GenerateXML action prior to assigning parameter values

GenerateXML action before parameter values

The values from the parameters come from the submitted form in the UserSiteRequest forms library. When you are adding this action to the workflow and want to define the parameter value, click the parameter name, for example, UserNTID. A Define Workflow Lookup window opens. You have several options to define the value for the parameter. Options include the Current Item (such as the item on which the workflow is initiated), Workflow Data (such as any workflow variables generated throughout the processing of the workflow, or any list within the current SharePoint site). Because the workflow was initiated on this particular item to begin with, in the case of the permission request the form object is defined as the Current Item in the Source list for any workflow lookup dialog box. Figure 25 shows setting the value for the UserNTID parameter. You should follow the same process to define the rest of the parameters in the GenerateXML action.

Figure 25. Define Workflow Lookup variable selection dialog box

Define Workflow Lookup dialog box

After defining all of the steps in the workflow and characterizing all of the actions with required parameters, you can deploy the workflow to the SharePoint list or document library.

Custom Workflow Deployment

After completely defining all of the custom workflow properties and actions, you can deploy the workflow directly from SharePoint Designer to the SharePoint list or document library that should process the workflow.

For the permission request solution, the following process was used to deploy the PermissionRequestWorkflow. After you define all of the properties and actions, click the Finish button at the bottom of the workflow designer window. This launches the workflow deployment dialog box, and shows the progress of the workflow deployment. After the deployment is complete, the workflow deployment dialog box closes and the workflow is deployed to the appropriate SharePoint list. Figure 26 shows the deployment dialog box during the deployment process.

Figure 26. Workflow Deployment dialog box with deployment progress

For the permission request solution, after all the pieces are in place, the employees can submit requests, the requests can be processed through the business workflow, and the request XML can be generated. After the request XML is generated for all of the approved requests, the XML files and the request information are processed on the MOSS server, and the employees are added to the Member group within MOSS. To complete the business process and give the employees Member permissions, you can use the console application to process the XML files automatically in a particular share as a scheduled task. The following section describes this final piece of the permission request solution.

UserAccessProvisioning Tool

The MOSS APIs allow you to add users programmatically to various groups within MOSS. The permission request solution requires that you add approved employees to the Member group on the MOSS server to complete the business process for granting employee SharePoint permissions.

You can use the UserAccessProvisioning tool to process these employee requests. The tool is a console application that runs as a scheduled task. The XML input files for the tool are stored on the hard disk of the MOSS server in a designated directory. The tool loops through the files located in this directory, reads through each file, extracts the values of the employee request into a UserRequestInformation object, adds the UserRequestInformationCollection object, and then loops through the collection object and adds every employee to the Member group by using the MOSS .NET API.

The utility class uses two support classes, UserRequestInformation and UserRequestInformationCollection, to contain the employee request information that is pulled from the XML files. The UserRequestInformation class is a simple data class that maintains the employee request information in a self-contained class. UserRequestInformationCollection inherits from the CollectionBase class to override the basic collection methods and properties. This collection class is a typed array class to contain all of the valid employee UserRequestInformation classes to add to the Member group. The code for these two data classes is contained in the code accompanying this article. For the specific files to review for this piece of the solution, see Summary of Key Sample Files.

The utility class first calls the LoopThroughFiles() method, which opens the appropriate directory and loops through all of the XML files contained there. Each XML file is then passed to the PullUserInfoFromXML() method that reads the XML from the file by using an XmlTextReader object and pulls all of the request information into a UserRequestInformation object. This object is then passed back to the LoopThroughFiles() method and the UserRequestInformation object is added to the UserRequestInformationCollection object. The code for these two methods follows.

private void LoopThroughFiles()
{
// Process the list of files found in the directory. 
string[] fileEntries = Directory.GetFiles(sourceDir);

foreach (string fileName in fileEntries)
{
//Get User Request information from XML file.
userRequestToPass = PullUserInfoFromXML(fileName);
}
}

private UserRequest PullUserInfoFromXML(string userRequestXML)
{
UserRequest newUserRequest = new UserRequest();
FileStream xmlStream = new FileStream(userRequestXML,FileMode.Open);
XmlTextReader reader = new XmlTextReader(xmlStream);

reader.Read();
reader.ReadStartElement("UserAccessRequest");
reader.ReadStartElement("UserRequest");

//Get User Domain.
reader.ReadStartElement("UserDomain");
newUserRequest.UserDomain = reader.ReadString();
reader.ReadEndElement();

//Get User NT ID.
reader.ReadStartElement("UserID");
newUserRequest.UserID = reader.ReadString();
reader.ReadEndElement();

//Get User Full Name.
reader.ReadStartElement("UserFullName");
newUserRequest.UserFullName = reader.ReadString();
reader.ReadEndElement();

//Get User Email.
reader.ReadStartElement("UserEmail");
newUserRequest.UserEmail = reader.ReadString();
reader.ReadEndElement();

//Get User Cost Center
reader.ReadStartElement("UserCostCenter");
newUserRequest.UserCostCenter = reader.ReadString();
reader.ReadEndElement();

//Get Approver Name.
reader.ReadStartElement("ApproverName");
newUserRequest.ApproverName = reader.ReadString();
reader.ReadEndElement();

//Get Approver Email.
reader.ReadStartElement("ApproverEmail");
newUserRequest.ApproverEmail = reader.ReadString();
reader.ReadEndElement();

//Get Personal Site
reader.ReadStartElement("PersonalSite");
newUserRequest.PersonalSite = reader.ReadString();
reader.ReadEndElement();

//Get Technical Site.
reader.ReadStartElement("TechnicalSite");
newUserRequest.TechnicalSite = reader.ReadString();
reader.ReadEndElement();

//Get Business Site
reader.ReadStartElement("BusinessSite");
newUserRequest.BusinessSite = reader.ReadString();
reader.ReadEndElement();

return newUserRequest;
}

The utility class then calls the AddUsersToSite() method, which opens a connection to the appropriate SharePoint site, loops through the UserRequestInformation objects in the UserRequestInformationCollection object, and adds each employee to the Member group. Finally, it sends an e-mail message to the employee notifying him or her that the request is processed. The code for this tool is in the sample that accompanies this article; however, the code for the AddUsersToSite() and EmailUser() methods follows.

private void AddUsersToSite()
{
UserRequest currentUserRequest = new UserRequest();

try
{
SPSite siteCollection = new SPSite("http://" + thisServer);
      SPWeb mossWeb = siteCollection.OpenWeb();
      SPGroup membersGroup = mossWeb.SiteGroups["Litware Inc. Members"];
      
foreach (UserRequest newRequest in userRequestsToAdd)
      {
      currentUserRequest = newRequest;
membersGroup.AddUser(newRequest.UserDomain + "\\" + newRequest.UserID, 
newRequest.UserEmail, newRequest.UserFullName, "");
         importLog.LogMessageToFile("User added to members group: " + 
newRequest.UserDomain + "\\" + newRequest.UserID + " - " + 
newRequest.UserFullName);
         EmailUser(newRequest.UserEmail);
      }
}
catch (Exception ex)
{
importLog.LogMessageToFile("Error adding user: " + 
currentUserRequest.UserDomain + "\\" + 
currentUserRequest.UserID + " - " + 
currentUserRequest.UserFullName + ". Exception: " + 
ex.InnerException.ToString());
}
}

private void EmailUser(string userEmail)
{
SmtpClient client = new SmtpClient("moss");
   MailAddress from = new MailAddress("administrator@litwareinc.com", "LitwareInc 
Administrator",System.Text.Encoding.UTF8);
   MailAddress to = new MailAddress(userEmail);
   MailMessage message = new MailMessage(from, to);
     message.Body = "You have been given permission to create sites in 
SharePoint.  Please go to this link to create your SharePoint site: 
http://" + thisServer + ".";
   message.BodyEncoding = System.Text.Encoding.UTF8;
   message.Subject = "Permission to Create SharePoint Site Approved";
   message.SubjectEncoding = System.Text.Encoding.UTF8;
   string userState = "Permission to Create SharePoint Site Approved";
   client.SendAsync(message, userState);
}

After the requests are processed, all of the XML files are uploaded to a request archive document library by using the UserRequestUpload tool, and the XML files are deleted from the directory on the MOSS server.

Summary of Key Sample Files

The UserAccessProvisioningUtility subfolder in the SampleFiles directory contains all of the code for the console application used in the permission request solution to add users to the Member group in MOSS. The solution and project files are also included in the UserAccessProvisioningUtility folder. To view the code, you should open the UserAccessProvisioning.sln file from Visual Studio 2005.

UserRequestUpload Tool

The business process for the permission request solution requires that you archive the XML files that summarize each employee request within a SharePoint document library. You can use a console application to run as a scheduled task and upload the XML files to the SharePoint archive document library.

The UserRequestUpload tool is a simple console application based on the .NET Framework, which loops through the files located in a particular directory on the hard disk of the MOSS server, and then uploads each file to the SharePoint archive document library. The SaveToSharePointReports() method is the primary method of the UserRequestUpload tool. The entire UserRequestUpload utility class is available in the code accompanying this article; however, the code for the SaveToSharePointReports() method follows.

public void SaveToSharePointReports()
{
try
{
string[] fileEntries = Directory.GetFiles(sourceDir);
foreach (string fileName in fileEntries)
{
//Start Uploading IDSSearch Report.
FileStream stream = File.OpenRead(fileName);
byte[] content = new byte[stream.Length];
                    
//Read the file into a byte array.
stream.Read(content, 0, (int)stream.Length);
stream.Close();

//Name of the document when saved in the library.
string fileNameTrimmed = 
fileName.TrimStart(("E:\\UserRequests\\").ToCharArray());
                    
SettingsProperty SPSLibrary = new 
SettingsProperty("SharePointReportsLibrary");

SPSite site = new 
SPSite(Project.Properties.Resources.SPArchiveSite.ToString());
SPWeb web = site.OpenWeb();

//Get the folder that should store the document.
SPFolder folder = web.Folders["UserSiteRequestArchive"];

//Upload the File.
SPFile myFile = folder.Files.Add("User" + fileNameTrimmed, content);
importLog.LogMessageToFile("User request file uploaded: " + "User" + 
fileNameTrimmed);
}

}
catch (System.Exception ex)
{
importLog.LogMessageToFile("ERROR: " + ex.InnerException.ToString());
}
}

Summary of Key Sample Files

The UserRequestUploadUtility subfolder in the SampleFiles directory contains all of the code for the console application used in the permission request solution to upload all of the processed user requests to the SharePoint archive document library. The solution and project files are also included in the UserRequestUploadUtility folder. To view the code, you should open the UserRequestUpload.sln file from Visual Studio 2005.

Conclusion

This article examines some of the capabilities that are built into Microsoft Office SharePoint Server 2007 Enterprise Edition, which enable business process automation and simplify the business process. This article shows the tight integration between the MOSS portal, including InfoPath Forms Services and the Business Data Catalog, and various other 2007 Office system applications and features, including InfoPath 2007 and SharePoint Designer 2007. This solution also shows how programmatic access and manipulation of various MOSS components—such as the Member group and user permissions, and the Business Data Catalog and legacy LOB data sources with the built-in SharePoint APIs—enables this type of business process automation. The automation was developed primarily by using Visual Studio Tools for Office 2007 Second Edition and the Visual Studio 2005 Extensions for Windows Workflow Foundation. Additionally, you can develop traditional .NET Framework console and Windows Forms applications to make use of the MOSS and 2007 Office system APIs that facilitate the automation and simplification of business processes within the enterprise.

Additional Resources

Resources for Developers

How-To Resources