![]() |
|
Spaces home Chris's SpacePhotosProfileFriendsMore ![]() | ![]() |
Chris's SpaceRandom information on software development
|
|||||||||||||||
WSS 3.0 Workflow Bulk Task ProcessingOne of the most common questions I get when someone has worked with WSS 3.0 Workflow is, "How can I approve or reject all of my tasks on one screen?". They're tired of clicking one task, approving it, then clicking the next one and so on. All they want is a simple page that will list all of their tasks in one place and allow them to run down the list checking approve or reject and submitting when their done. There is a bulk approval option using InfoPath forms, but in my opinion it's not that useful. You can create a form that displays common controls. For example you can click an approve or reject button and approve or reject all of your tasks, but have no control over an individual task. Once you've made your decision the submission goes into a queue and is processed later. This isn't what they were looking for. In response to several requests by students in my past classes (you know who you are), we added support for bulk approvals in the WSS3 Workflow Tools CodePlex project. Using the same ASCX forms based approach I described in a previous post, we created a host page that will easy the process of creating bulk task processing forms. WSS3 Workflow Tools Bulk Task ProcessingThere were two key aspects of the built in support for bulk processing that needed to be remedied. First, access to the bulk processing should happen from the list containing the workflow. Second, the task processing form should allow different actions to be performed on each task. The solution to this was to build a special feature that added a new item to the actions menu of document libraries that directs the user to page of bulk task links. On this page the user will get a list of all the types of tasks that can be bulk processed. Once the user selects the type of task to bulk process they click the link and are directed to a final page that will display the all the tasks. This form is the only part that is defined specifically for the task being processed. The job of the form is to display the tasks using data from the framework and gather the results and pass them back to the framework. Building a Custom Bulk Task FormAdding a custom bulk form to a project that already has a task form in place is a simple process made up of 2 steps:
Creating a custom bulk task form is similar to creating a custom task form, with one difference; you're displaying multiple tasks instead of just one. This is handled by the TaskItemCollection class. When creating a custom task form, your .ASCX file derives from TaskControl which loads and saves using a TaskData object. When working with multiple tasks we need to return multiple TaskData objects. We also need to be able to identify a task, so a task id needs to be attached to each entry. This is handled by using a TaskItem class which derives from TaskData and then a TaskItemCollection that contains multiple TaskItem objects. The other difference between a task form and a bulk task forms is the base class. Bulk task forms derive from BulkTaskControl. Once the .ASCX file derives from BulkTaskControl, the TaskItemCollection is bound to a ASP.NET Repeater control and the task data is displayed on the screen. In this example I chose to use a drop down list to select the type of action to perform on the task. When the page loads, the LoadData method binds the TaskItemCollection returned by the GetData method to the Repeater control. The last step in creating the form is to extract the data from the form and submit the changes to the framework. Since the base class BulkTaskControl expects a TaskItemCollection, it will need to be created in the SaveData method and provided back to the framework. One detail to note in the code, make sure the AllowExtendedProperties property is set to true. If you don't set this value and try to use the example (which is based on task extended properties, not content types) it will fail. Now that the form is complete, all that's left is to register the bulk task forms in the workflow.xml file. Just like all of the forms in the WSS 3 Workflow Tools project, bulk task forms are registered using the workflow template's metadata. The first two entries required allow the framework to determine if bulk task forms exists so the feature button can be enabled. These metadata tags are Task_BulkIds and Task_nName. The Task_BulkIds is a comma delimited list of integer ids of tasks to be used that will map directly to the Task_nName metadata tags. For each number in the id list, one name entry exists. This lets the framework know whether to display the feature button and how to name the tasks in the following display page. Once the user has chosen the task to process in bulk, the framework will need to load the appropriate .ASCX file. This is where the BulkTask_nFormUrl is used. The number in the entry maps to the same id that the task form control receives. That's all it takes to add a custom bulk task for to an application that already has a task form. The goal of this framework was to make the process of building a bulk task form as simple as possible to allow you to focus on the business logic of your task and page and not the framework. For more information check out the WSS 3.0 Workflow Tools project website on CodePlex. You can download the sample code here. WSS 3.0 Workflow Task FormsUsing the WSS 3.0 Workflow Tools to add task forms to a WSS 3.0 workflow is a simple process. Just like Association and Initiation forms, the Task form is based on a hosted .ASCX control in a standard hosting page. The primary difference between association/initiation forms and task forms is how the data being transferred is stored. Association/Initiation forms use a string to transfer data between the WSS 3.0 framework and the forms. What is stored in this string is entirely up to the developer, although the data is commonly stored as XML. In Task forms, the data being transferred is stored as a set of named properties. How these properties are stored is based on which method of task creation is chosen. There are two ways to store data in workflow tasks: custom content types and extended properties. The primary difference between the two methods is how strongly typed the data is. Content types provide strong typing but requires that all fields in the task be defined at design time. Extended properties don't provide the same level of typing but allow items to be stored in a property bag. The WSS 3.0 Workflow Tools support both methods. Creating a Workflow TaskFor those of you that are already familiar with creating workflow tasks, you can skip this part. For those of you that aren't this will be a quick introduction to creating workflow tasks and waiting for them to change. The key steps in using workflow tasks are to create the task, wait for it to change, and complete the task. Each of these steps are performed with special SharePoint workflow activities:
In this example, the task is being created and then the workflow waits for it to change. As soon as it changes the workflow assumes it's done. In a real world scenario, the OnTaskChanged is often placed in a While look that will continue to look until some value in the task is set. At that point, the task is completed and the workflow continues, but for now this simple scenario will work. Creating a custom Task formCreating a custom task form is very similar to custom Association and Initiation forms. The only difference is the base class. Instead of deriving from ConfigurationControl, you derive from TaskControl. The only difference between these two classes is the object it returns from the GetData and SaveData methods. In the ConfigurationControl this class is ConfigurationData in TaskControl it's the TaskData. TaskData is the class the contains the logic that allows tasks to retrieve their values from a Content Type or extended properties. To access a value the overloaded[] operator is used to lookup a value from the content type or extended properties. TaskData also implements a custom TypeDescriptorProvider which allows the TaskData to be bound to using standard ASP.NET data binding constructors. The code below shows the markup for the page. Notice how the buttons utilize the CommandName of Commit to indicate to the framework the operation to perform and how they use the CommandArgument to indicate whether the button indicates approval or rejection. This command argument will be used later in the SaveData method. The code that supports the page overrides the LoadData and SaveData methods just like the Association or Initiation forms but this time the methods operate on a TaskData object. In the LoadData method the value MyFieldValue is used to populate the form. In the SaveData method the value is extracted from the form and placed back into the TaskData. There's also a Result value that's placed in the taskData object and it comes from the commandArgument parameter. This parameter is automatically provided to the form developer by the framework. When a button is clicked and it has a CommandName of commit, the CommandArgument value is provided to the SaveData method in the commandArgument parameter. This gives form developers a way to utilize the frameworks event handlers but still provide a value to the code handling the event. The last step is to register the form in the Workflow.xml file. This process is a little more complicated than the Association/Initiation forms. Since the form is attached to the task and not the workflow, a custom content type is required for task forms. The framework already provides a content type that maps the default task forms to the frameworks host forms. Setting the TaskListContentTypeId attribute in the Workflow.xml file tells the WSS 3.0 workflow framework which content type to use for new tasks. Now the custom task forms need to be mapped to the workflow using more metadata entries in the workflow template defined in the workflow.xml. This is done using the Task_nFormUrl tag, where n is the id of the task. The reason an ID is needed is to differentiate between multiple tasks in the same workflow. When a workflow is created using CreateTask a TaskType integer property is set that determines the type of task. When the custom form Url is retrieved, the TaskType value is used for n in the metadata tag. For example if the form for TaskType 0 is required, the metadata tag is b. Now that these changes are made, the custom task forms are ready to execute. I'll be adding another blog entry shortly to discuss how to process multiple tasks in the same UI, so instead of having to manually process several tasks in separate pages, all the tasks can be processed in one page. The code for this example can be downloaded here. WSS 3.0 Workflow Training with the Ted Pattison GroupI'll be teaching a WSS 3.0 Workflow course in Irvine, CA on August 25th. Anyone needing a deep dive into the details of how to build custom workflows using VS.NET and create custom forms using ASP.NET or InfoPath might want to check it out. If you're interested, you can get the first module on the Ted Pattison Group's site. Building WSS 3.0/MOSS Workflow Forms in ASP.NETOver the last few months I've been working with Wouter van Vugt on a project named WSS 3.0 Workflow Tools. The goal of this project is to simplify the process of creating a new WSS 3.0 workflows with custom forms. To achieve this goal, the project uses a combination of base classes, custom ASCX user controls and Visual Studio Templates. Update: Wouter has published some nice videos of the tools in use. Check the out here. Process Management in SharePointBack in the days of WSS 2.0 process management was handled using event handlers and custom pages. There was no way to easily create, manage, and deploy a business process from a single location. Luckily this was remedied in WSS 3.0. Using Windows Workflow Foundation and some custom forms, now you can easily model business processes using a visual process designer and expose them via WSS 3.0. This all sounds great until you want to write your own custom forms to manage starting processes and interacting with any tasks user's need to perform. Currently WSS 3.0 offers two options for creating your own forms. The first is writing custom ASP.NET pages. This approach requires that you build custom ASPX pages and register them with your workflow. The second is to use InfoPath Forms Services 2007 and host custom InfoPath forms in your browser. Each approach has its pro's and con's.
If you already have MOSS, the InfoPath solution works well, but if you don't already have it or you're building a solution and you'd like to target the largest possible audience, ASP.NET forms are the way to go. Forms in the WSS 3.0 Workflow projectThe WSS 3.0 Workflow project aims to simplify the development of WSS 3.0 Custom ASP.NET Workflow Forms by abstracting as much of the complexity of development into the framework. This allows the developer to focus on the domain specific aspects of the form rather than the standard code and layout that comes with all WSS 3.0 Workflow Forms. Building a Custom ASP.NET form consists of several steps:
Each of the forms created have a different purpose, but any two implementations of one page type are very similar. For example if I were to write two Association Forms for two very different workflows, the visual design and page logic code would differ, but the common WSS 3.0 API integration would be very similarly. These similarities immediately lead to common base classes being created that allow the functionality of the pages to be abstracted away. Now the job of building a Custom Form is down to creating the visual design and deploying the new page. The complexity of the WSS API integration are all hidden in a set of base classes that allow the page design process to become much simpler. While these base classes dramatically speed development of custom ASP.NET Workflow Forms, they don't help with the creation of a consistent visual layout. Unfortunately this is one of the most tedious processes in the development of custom ASP.NET Workflow Forms. Currently Visual Studio 2008 does not have a visual design environment for WSS 3.0 pages, so development is done by changing the .ASPX page and refreshing the browser. To meet the goal of the framework, a way needed to be found to simplify the process of building the visual page as well. InfoPath Workflow Forms use a hosting process to embed an InfoPath form rendered as HTML inside a standard hosting page. For example when you load an InfoPath Form, the ASP.NET page you load is ?CstIPFrm.aspx?. This page hosts a special control called ? that will render the InfoPath form inside the host ASP.NET page. This process is the same as the process used by the WSS 3.0 Workflow Project. Instead of creating your own custom page using the base classes provided, you can choose to create your own form using a .ASCX file. Once the ASCX file is completed, all you need to do is register the appropriate host page in the framework and register your .ASCX file as the form for your workflow. Using the WSS 3.0 Workflow Project's framework, the steps for creating your own page are now much simpler:
All you need to do is focus on your domain logic and leave the rest to the framework. Creating a simple Association FormThe first step in creating a new Association Form is to create a new .ASCX page in your project. Add any controls you need to capture the data specific to the workflow you're creating. In this example we're capturing the user name of the person who should approve the document as well as any instructions for that user. All .ascx controls intended for use as an AssociationForm need to derive from TPG.SharePoint.Workflow.ConfigurationControl. This class provides your control with all the information it needs to load and save your domain specific data. In this example the LoadData method is overridden to populate the control and the SaveData method is overridden to extract the data from the controls and send it to the framework. Finally the workflow.xml file containing the workflow feature needs to be updated. In this case the AssociationUrl attribute contains the path to the framework's shared Association Form hosting page. A special metadata entry with the key of Association_FormUrl will define the .ascx control that will be hosted inside the hosting page. This metadata entry is stored by WSS 3.0 in the SPWorkflowTemplate object and is accessible to the framework. The Association Host page will use this metadata element to lookup the path of your user control and create an instance hosted in the page. Once all these steps are complete and the new association page for this workflow is loaded, the framework will render all of the border for your page, handle looking up your custom user control, and displaying it for you. When any changes are made, the framework will call your code to extract the values from the form and handle the rest of the details for you. Other FeaturesThis sample only scratches the surface of the framework. The WSS 3.0 Workflow framework also contains several features such as:
Between Wouter and I, we'll be putting together samples highlighting each of these features and more. You can find the WSS 3.0 Workflow framework here and download the sample code here. Team Foundation Build and Web Deployment ProjectsI'm currently working on a project that uses the Web Deployment Project to precompile a library of .ascx controls. To do this, I used a Web Application Project (WAP) to create and test the controls, then deployed them using a Web Deployment Project (WDP). My other web site reference the .dlls generated by the WDB. Within Visual Studio 2005, this all works fine. VS is smart enough to understand that there is a dependency between the WDP and it's WAP and I manually added on between my site and the deployment project.
Then I moved this scenario into Team Build. I started to get errors from the deployment project telling me that the the project it's referencing didn't exist. Looking at the log files I found the Team Build was building the deployment project before the application it deploys! After a few hours of playing around I figured out there is a simple solution.
All you need to do is manually edit the .sln file to add an explicit dependency. My guess is that the WDP within VS2005 knows it's dependency, but since there is no explicit reference to the WAP, MS Build isn't aware of it. Also, since VS knows of the dependency you can't manually add the dependency within the UI.
To fix this problem, open up your .sln file in notepad and find the deployment project entry. Now add the ProjectSection(ProjectDependencies) section within the deployment project entry referencing the project you want to depend on.
Project("{...}") = "ControlProject", "ControlProject.csproj", "{1A6FB83E-0707-40E9-8D1D-2406D79D2A4C}"
EndProject Project("{...}") = "ControlDeploymentProject", "ControlDeploymentProject.wdproj", "{AC602F91-88F5-4030-A4E4-B86517E6A7C8}"
ProjectSection(ProjectDependencies) = postProject {1A6FB83E-0707-40E9-8D1D-2406D79D2A4C} = {1A6FB83E-0707-40E9-8D1D-2406D79D2A4C} EndProjectSection EndProject Now all you need to do is save and run your build process again.
|
|
||||||||||||||
|
|