88 articles and 812 comments as of Saturday, November 1st, 2014

Monday, March 15, 2010

How To: Use default SharePoint approve/reject InfoPath task form in custom Visual Studio workflows

Editor’s Note: In a new series of articles, I will be introducing you to some of the speakers who will be presenting at the SharePoint Evolutions Conference in London next month, pointing you to articles on their sites, offering downloads of white papers, and publishing articles on their subjects of expertise. — Mark Miller

Eugene Rosenfeld
Things that Should be Easy

I needed to write a customized approval workflow. I know I could use InfoPath to create custom approval task forms, and I saw many posts showing how this is done. But if I could use the SharePoint Approval Workflow task forms, I could save a lot of development time and deployment effort.

This post focuses on how I was able to use the out of the box InfoPath approval approve/reject task forms forms in my custom Visual Studio SharePoint workflow.If you’re looking for a general how-to post on creating SharePoint workflows with Visual Studio, this is not the post for you. I would instead point you to Serge Luca’s excellent 20-part series on creating SharePoint workflows with Visual Studio.

Creating the task

Here’s the skinny on what you need to do in order to use the default Microsoft Office SharePoint Server 2007 (MOSS) approve/reject InfoPath form in a custom Visual Studio workflow.

1. Create a SharePoint workflow project

There are many ways to do this, so I won’t go into details here. My personal favorite way is by using the WSP Builder Visual Studio workflow template project. Just make sure that your workflow class inherits from System.Workflow.Activities.SequentialWorkflowActivity or from System.Workflow.Activities.StateMachineWorkflowActivity.

2. Set the TaskListContentTypeId attribute

Edit the elements.xml file in your workflow feature definition. Set the TaskListContentTypeId attribute value to use the MOSS workflow task content type id:

<Elements>
    <Workflow
        Name="MyWorkflow"
        Description="This is my custom workflow."
        …
        TaskListContentTypeId="0x01080100C9C9515DE4E24001905074F980F93160"
        >
        …
    </Workflow>
    …
</Elements>

3. Add the Task_FormURN and ExtendedStatusColumnValues elements

Continue to edit the elements.xml file in your workflow feature definition. Add the following MetaData element under the Workflow element:

  <Elements>
    <Workflow …>
        <MetaData>
          <Task0_FormURN>urn:schemas-microsoft-com:office:infopath:workflow:ReviewRouting-Review:$Subst:LCID;</Task0_FormURN>
          <Task1_FormURN>urn:schemas-microsoft-com:office:infopath:workflow:ReviewRouting-Review:$Subst:LCID;</Task1_FormURN>
          <Task2_FormURN>urn:schemas-microsoft-com:office:infopath:workflow:ReviewRouting-Review:$Subst:LCID;</Task2_FormURN> 

          <ExtendedStatusColumnValues>
            <StatusColumnValue>$Resources:ReviewFeedback_CanceledStatus</StatusColumnValue>
            <StatusColumnValue>$Resources:ReviewFeedback_ApprovedStatus</StatusColumnValue>
            <StatusColumnValue>$Resources:ReviewFeedback_RejectedStatus</StatusColumnValue>
          </ExtendedStatusColumnValues>
        </MetaData> 

        …
    </Workflow>
    …
  </Elements>
  

4. Add a CreateTask activity to your workflow

Add a CreateTask activity to your workflow. Make sure to set the TaskType property on the TaskProperties of the CreateTask activity to 1. This will tell the workflow runtime to create the task using the Form Urn corresponding to the inner text in the Task1_FormURN element. The CreateTask activity’s MethodInvoking event is a good place to set the property.

private void createTask1_MethodInvoking(object sender, EventArgs e)
{
    //initialize task infrastructure data
    this.createTask1_TaskId1 = Guid.NewGuid();
    this.createTask1_TaskProperties1 = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
    this.createTask1_TaskProperties1.TaskType = 1;

    //set task remaining properties
    //...
}

Processing a workflow task

Now that you’ve created a workflow task that uses the default approval form, you will want to your workflow to do something useful when a user approves or rejects the task. Here is how to detect when a user approves or rejects the workflow task.

5. Detect task change with OnTaskChanged

This is fairly straight forward: add an OnTaskChanged activity to your workflow. Set the task ID and correlation token to correspond to the ones used when creating the task in the previous section. See Serge Luca’s SharePoint workflow series for details.

6. Task approved or rejected

Here’s the interesting part. Whether a user approves, rejects, reassigns, or requests a change in the task form, the task is always marked as Complete. The actual value that corresponds to what the user selected is stored in the TaskStatus in the ExtendedProperties collection of the SPWorkflowTaskProperties class. The following is a table that correlates the user’s approval action to the value of the TaskStatus extended property:



User Action
TaskStatus Value
Approve
“#”
Reject
“@”
Cancel
[no change]

Here’s a sample helper method that checks whether a particular workflow task was approved:

public static bool IsTaskApproved(SPWorkflowTaskProperties TaskProperties)
{
    return TaskProperties.ExtendedProperties["TaskStatus"] != null
        && TaskProperties.ExtendedProperties["TaskStatus"] as string == "#";
}

Anything else?

That’s it. There is surprisingly little work involved in using the default MOSS approvals forms in your own custom SharePoint Visual Studio workflows, once you know what to look for that is. I’d like to wrap us this post with the benefits to this approach and a few things you should keep in mind when working with the default workflow forms.

Some benefits to this approach

Here are just some of the benefits you get when you reuse the existing MOSS approval task forms rather than creating your own customs InfoPath task forms:

  • No InfoPath forms development
  • No InfoPath forms deployment
  • Out of the box user interface for:
    • Approve
    • Reject
    • Cancel
    • Reassign Task
    • Request a change
    • Instructions to approver
    • Capture approver comments
  • Out of the box multi-language user interface with appropriate MOSS language packs. Notice the references to “$Resources:”.

A few things you should know

  • Any action in default task forms except Cancel marks the task “Complete”. Your workflow must then create a new task with the desired action from the completed task. For example: If a user re-assigns the task to a new user, the task for the user is marked complete. Your workflow must create a new task and assign it to the new user.
  • The MOSS task forms derive most of their data from fields in the ExtendedProperties of the task, not the main task properties.

Guest Author: Eugene Rosenfeld
Things that Should be Easy

Eugene Rosenfeld is the CTO of Black Blade Associates. He started his IT career as a database programmer and soon moved into enterprise application integration (EAI) and portal systems. Eugene holds a strong belief that all systems should have the innate ability to intelligently communicate with one another. Most recently he has been heavily involved with distributed systems architecture, Services Oriented Architected (SOA), peer-to-peer systems, and cloud computing.

Eugene has been recognized for his community involvement by Microsoft through their prestigious MVP program. His MVP profile is available at https://mvp.support.microsoft.com/profile/Eugene.Rosenfeld.

Bookmark and Share
 

Please Join the Discussion

33 Responses to “How To: Use default SharePoint approve/reject InfoPath task form in custom Visual Studio workflows”
  1. Veera says:

    Hi Eugene,
    I followed your steps. When i started the workflow I am getting Failed on Start (retrying) error. I tried to debug the workflow but it does not break any where while creating the Workflow task.

    Please let me know if I am missing anything..

    Thanks,
    Veera.

    • Veera,

      If the workflow does not start, you will never hit a breakpoint. Unfortunately the Failed on Start (retrying) is SharePoint workflow’s catch-all error message. It could mean anything. The best place to check for clues as the nature of the error is in SharePoint’s ULS logs. They are text files located in:

      %Program Files%\Common Files\Microsoft Shared\web server extensions\12\LOGS

      Search for text such as “error” or ” at” to quickly get to the error messages.

      -Eugene

  2. Ben says:

    I gave this a try and managed to get it working. However, I noticed that contributors can approve their own work. I have checked all the permissions and this should not be occurring.

    • Ben, you could try adding a code activity to your workflow after the CreateTask activity to set custom permissions on the task that just got created. This will let you apply very granular rules as to who is able to modify which workflow tasks.

      -Eugene

  3. Mario Fulan says:

    This is a fantastic sample. I got it working in just a few minutes time. However, I didn’t follow how to handle the “reassign task”. Is there a sample somewhere you can point me to? How do I know the user hit the “reassign task” button?

    • The re-assign task doesn’t work quite as you might expect. Rather than assigning the current task to a new person, re-assign task actually completes the current task with a specific TaskStatus, then creates a new task for the new person and copies the data from the current task to the new task. I don’t know what the TaskStatus for task re-assignment is, but you can find out the same way I did: Start an approval workflow, reassign a task, then inspect its properties using a tool like SharePoint Manager:

      http://spm.codeplex.com/
      http://weblogs.asp.net/gunnarpeipman/archive/2009/03/11/sharepoint-manager-2007.aspx

      -Eugene

      • Taufan says:

        hi Eugene,

        i still not understand how build U Reassign A task with reassign task?

        i mean how u Design u ur workflow activity ?

        can u send me some code?

    • Rene says:

      Mario — where you ever able to figure out how to determine if a Reassign or RequestChange was triggered? I’m having the same issue and as best I can tell neither Reassign nor RequestChange update the “TaskStatus” property with anything. Is that what you found? How (if at all) did you resolve this?

      Thanks

      rene

  4. Ramya says:

    Hi,
    Did you get the answer for you r question?
    Even i have a question in retrieving reassign task and change request values in the workflow.
    Any help is appreciated.

  5. Ran says:

    can this be applied in workflow created by sharepoint designer ?

    • That’s a great question. As far as I can tell, SharePoint Designer does not leverage InfoPath Web Forms technology for its workflow forms. I assume the reason is that SharePoint Designer needs to work with WSS and InfoPath Web Forms are only part of MOSS.

  6. Sorry for not responding to comments. Ramya’s comment was the first one that sent me an email notification. I will respond to comments shortly.

    -Eugene

  7. Taufan says:

    hi Eugene,

    i have problem if i want to build custom workflow Reassign Task. could u show me how to do that,

    when we use approver – sharepoint 2010 and click reassing task, current task will be completed and the new task will be create.

    how to do that in custome workflow???

    • Hi Taufan,

      As with just about everything you do in custom Visual Studio workflows, you will need to resort to code to have your workflow mimic the same reassign task behavior as the SharePoint workflows. When the initial task is completed, you will need to check its status. If you detect the ExtendedProperties["TaskStatus"] as string is a Reassign code, you will manually need to create a new task, assign it to the correct person, transfer the title, body, and other task properties, and then check the ExtendedProperties["TaskStatus"] as string of the new task you just created to see the result. You will have to continuously repeat this process until the ExtendedProperties[TaskStatus] as string is the code for either Approved or Rejected.

      I know its cumbersome, but this is the way the OOTB MOSS workflow forms operate. The easiest way to continuously check the ExtendedProperties[TaskStatus] property in a Sequential workflow is with a loop activity. A State Machine workflow might give you a more elegant implementation.

      I don’t have a code sample with Reassign task available at the moment. I have thought about putting a sample together, but I am swamped at work right now, so it will be some time before I get a sample out there. Ben and Mario have both gotten things working according to my concepts. Perhaps they can post their code until I can put a more formal sample together.

      -Eugene

  8. Siva says:

    Hi,

    I need Develop the workflow as like the same as out of box Approval workflow with Re-Assigning Task, Please guide me, how can i develop the Re-Assigning Task workflow using Visual Studio or Sharepoint Designer, What are the activity i have to use for that? and How to use?, Please provide me some sample code

    Thanks.
    Siva

    • Hi Siva,

      Recreating the OOTB SharePoint approval workflow is not a trivial task. You would need to decode all of the data serialized the way I did with the TaskStatus. As I mentioned to Mario, the SharePoint Manager tool on Codeplex is a good place to start:

      http://spm.codeplex.com/
      http://weblogs.asp.net/gunnarpeipman/archive/2009/03/11/sharepoint-manager-2007.aspx

      As I mentioned to Taufan, the OOTB workflows complete all workflow tasks and create a new task for any task action, including re-assignment. Your best bet in mimicking this behavior is to use a loop activity or a state machine workflow. Just make sure to watch how you handle your correlation tokens as SharePoint workflow is picky about tokens, particularly when the tokens are in a loop.

      -Eugene

  9. maz says:

    Hi Eugene,

    This is a really helpful article – thanks for taking the time to write it.
    I was wondering if this same approach and procedure is directly applicable in a SharePoint 2010 environment?

    Could you confirm if you have tried this, and if not, can you suggest some ways I could consider doing this with SharPoint 2010 and Visual Studio 2010.

    Regards.

    • Maz,

      I have not tried this with SharePoint 2010 yet. In part, this is because SharePoint 2010 has such a richer workflow capability, that I don’t yet know if this is even needed with 2010. You can see some of the enhancements to SharePoint 2010 workflow here:

      http://msdn.microsoft.com/en-us/library/ee537015.aspx

      -Eugene

      • maz says:

        Eugene,

        Thanks for your reply. I ask as I have been trying to get a custom workflow (developed in visual studio 2010) deployed and working in a SharePoint 2010 instance.
        However, it always fails when the workflow is initiated: an exception message about no formURN being specified – I have looked around for a solution – the only (clearly) documented way I have found is the approach you outlined for MOSS 2007 (MSDN seems very content light in some areas). I really do not want to have develop new aspx pages or InfoPath forms, so if you do find something applicable for 2010 in the future, then please post it as it would solve a big headache!

        Regards.

  10. Taufan says:

    HI Eugene,

    its me again hahahha

    there is possible to associate custom workflow to custom Content Type???

    thanx

  11. Oriol says:

    Hello Eugene,

    thats a great post. I have it partially working in a few minutes. But I have a problem.

    When the form is opened, in place of appearing Approve/Reject/Cancel buttons, the buttons that appear are Send Feedback/Cancel.

    Do you know why it is this happening?

    thank you lots.

    • Oriol, I would check to make sure that your Task_FormURN registrations are correct and that you are using the correct TaskType index before you create your task.

      • Oriol says:

        Hello Eugene,

        First of all, thank you for your response.
        It was my fault, I was assigning the task type id to a different task (not to the one I was testing).

        Thank you for all again!!

  12. Rene says:

    Hey there — great example….once i got the correct settings in my CreateTask activities it worked beautifully!

    Couple questions:

    1. In the elements.xml youi have three URN entries and the all have the same settings — do these entries allow me to change the button labels at runtime? I.e. for Task0_FormURN I would like the buttons as you have them — Approve, Reject, Cancel. But for Task1_FormURN I would like to see only two buttons (for example) — Resubmit and Cancel (I would use this for a scenario where the reviewer requested additional info and the originator responds). Is this possible?. If so, where/how do I set the corresponding entries?

    2. How/where can one see the actual form definition so that I can determine what the various fields are so I can preset them? Does that make sense? In my code I set:

    Reviewtask_TaskProperties1.Title = “Pending CAPEX Request Review [Review Gate " + _curGateNum + "]“;
    Reviewtask_TaskProperties1.AssignedTo = _curGateReviewer;
    Reviewtask_TaskProperties1.Description = “The attached CAPEX Request requires your review and approval”;
    Reviewtask_TaskProperties1.ExtendedProperties["comments"] = “”;
    Reviewtask_TaskProperties1.ExtendedProperties["instructions"] = “Please review and approve/reject as appropriate”;

    THis does properly preset Title and AssignedTo, but the “Description” field does not appear to map to anything and so that’s not being preset.

    3. I’m using this form for a workflow that actually has an attached InfoPath form that is being routed — dio you know what/how/where I need to make a change so that the attachement is opened as a browser-enabled form and NOT in the InfoPath application? THe form is browser-enabled, and when I create it/edit it within the form library it exists in, it is opened as a browser-form. Only when I click on the attachment link associated with the task does it attempt to open InfoPath.

    Thanks for any help you can provide.

    rene

  13. Rene says:

    ONe more question if I may — I’ve been trying to figure out how to deal with the reassign and request change options, but it does not look like these provide a status value like Approve and Reject….As you indicated Approve and Reject return # and @, respectively. But when I step through my code and look at the ExtendedProperties values, I see # and @ for Approve/Reject, but the other options simply return a blank status.

    I assume that the ExtendedStatusColumnValues ($Resources:ReviewFeedback_RejectedStatus) set in the element.xml define what those values are that are returned? If so, how/where do I specify what to return for Reassign and Req Change? Where do I set the appropriate .mapping for “$Resources:ReviewFeedback_RejectedStatus”?

  14. calvin says:

    Hi,
    Did you get the answer for you r question?
    Even i have a question in retrieving reassign task and change request values in the workflow.
    Any help is appreciated.

    Regards,
    Calvin

  15. Amal says:

    Eugene,
    I have created Declarative workflow in SharePoint Designer 2010 and then imported it to Visual Studio 2010. There were some missing pieces after the import like dll references, InfoPath form packaging, associating the workflow forms.
    I have completed all the missing pieces.

    I am able to associate the workflow to a content type. The workflow is getting started but the Approval Infopath Form has blank values in the fields like request by, comments etc..

    I am using a custom approval content type which is assigned to the TasklistContenttypeId of the workflow element file.

    Any idea why the Approval form is displaying blank values? Can you guide to resolve the issue?

    Best Regards,
    Amal

Subscribe without commenting

Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!