So, as it turns out it's incredibly helpful to be able to see all of the workflow approvals relative to a specific object; I call this "simple" reporting and it's quite easy to setup. While my example shows how to do this for a Group object, you can really apply this tab to any object that you are collecting approvals on because the query is not object type specific. Here are some items of note before we look at the code:

  • The example shows how to query both the Approval object and the ApprovalResponse
  • The example shows the control binding RightsLevel to the SIDHistory attribute
  • The example also shows how to use the ListFIlter Property of a UoCListView control

Table of Contents



Anatomy

There are four objects at play when we're talking about the typical Owner Approved Group scenario:

Object Type Description
Request This is the original Request object created when the request to join/leave a group was submitted
Approval When your Approval activity fires it will generate an Approval object for each of the Owners you configured
ApprovalResponse For each person that either Approves or Rejects the request, an ApprovalResponse object is created
Group Once all of your approvals are completed and the threshold is met, you get the Group object

Again, I'm using a typical Owner Approved Group scenario here to illustrate, but this could just as easily be your own custom object which you've linked an Approval activity to. While you cannot yet use the Outlook Add-ins to join a custom object, the approval process works on any object type. For the purposes of reporting, we will go after the Approval and ApprovalResponse objects.

Security

Approvals can be sensitive in nature so you may decide that not everyone should be able to see who approved, or rejected, any given approval. If you don't care, then you can bind the control to any attribute that all users can see, say for instance: DisplayName. If you do care, then you will want to bind this to an attribute that is restricted and then apply your rights granting MPR's accordingly. You apply either situation by modifying the bolded following line in the code:

<my:Control my:Name="ApprovalView" my:TypeName="UocListView" my:Caption="All Approval Requests" my:ExpandArea="true" my:RightsLevel="{Binding Source=rights, Path=SIDHistory}">

Whichever attribute you bind to will control whether or not the control itself will appear. If a user cannot see any controls on a Group, the RCDC will not display the tab at all. In my case, I had a situation where only administrators should be able to see the data and SIDHistory is an attribute that is not explicitly granted any rights in any default MPR's, so only members of the Administrators Set would be able to see it anyway.

NOTE: Keep in mind that being able to see the attribute you are binding the RightsLevel to is only half of the equation. If you want non-administrators to be able to see the items in the report, you will also have to grant them rights to Read the relevant attributes on both the Approval and ApprovalResponse objects.

Filter

I mentioned that we make use the ListFilter property in the control, that's this line of the code:

<my:Property my:Name="ListFilter" my:Value="/Approval[Request = /Request[Target='%ObjectID%']]" />

...and this one:
<my:Property my:Name="ListFilter" my:Value="/ApprovalResponse[Approval = /Approval[Request = /Request[Target='%ObjectID%']]]" />

In the first filter we are looking for all Approval objects filtered by the Request attribute which contains a reference to a Request object whose Target is the object we are viewing. Much like the first filter, in the second filter we are looking for all ApprovalResponse objects filtered by the Approval attributes which contains a reference to a Request object whose Target is the object we are viewing. The difference here is that we're looking at the relationship between the Approval and the ApprovalResponse.

In either case, I'm illustrating how we use two separate controls to show the Approval and ApprovalResponse objects relative to the object we're viewing (say a Group). But as you can see, there is nothing object specific in the query so it can be applied to any object type.

To apply this grouping, you will need to edit the View and/or Edit RCDC's for your object and insert the code block peer to the other <my:Grouping headers. Hope this helps, for more information on editing your RCDC, please refer to the FIM 2010 RCDC XML Reference.

Oh, and always back up your RCDC's before you start editing them in case you need to fall back!

Once completed you should see something like this (note that my example depicts a custom object type):

 



Code

<my:Grouping my:Name="GroupingApprovals" my:Caption="Approvals" my:Description="This page displays two controls: 1) Approval Requests - These are requests to modify the membership of this group, the Approval Status reflects the final outcome of the request. 2) Approval Responses - These are the responses filed by the approvers. Note, however, that there may be multiple responses for a single request and an Approval does not indicate that the request itself was approved.">
  <!--Approval Reporting Grouping by Brad Turner, bst2k@hotmail.com-->
  <my:Control my:Name="ApprovalView" my:TypeName="UocListView" my:Caption="All Approval Requests" my:ExpandArea="true" my:RightsLevel="{Binding Source=rights, Path=SIDHistory}">
    <my:Properties>
      <my:Property my:Name="ColumnsToDisplay" my:Value="DisplayName,CreatedTime,Requestor,Approver,ApprovalStatus" />
      <my:Property my:Name="ResultObjectType" my:Value="Approval"/>
      <my:Property my:Name="EmptyResultText" my:Value="There are no approvals for this group." />
      <my:Property my:Name="PageSize" my:Value="7" />
      <my:Property my:Name="SearchControlAutoPostback" my:Value="false" />
      <my:Property my:Name="SearchOnLoad" my:Value="true" />
      <my:Property my:Name="ShowTitleBar" my:Value="true" />
      <my:Property my:Name="ShowActionBar" my:Value="false" />
      <my:Property my:Name="ShowPreview" my:Value="false" />
      <my:Property my:Name="ShowSearchControl" my:Value="false" />
      <my:Property my:Name="EnableSelection" my:Value="false" />
      <my:Property my:Name="SingleSelection" my:Value="false" />
      <my:Property my:Name="ItemClickBehavior" my:Value="ModelessDialog" />
      <my:Property my:Name="ListFilter" my:Value="/Approval[Request = /Request[Target='%ObjectID%']]" />
    </my:Properties>
  </my:Control>
  <my:Control my:Name="ApprovalResponseView" my:TypeName="UocListView" my:Caption="All Approval Responses" my:ExpandArea="true" my:RightsLevel="{Binding Source=rights, Path=SIDHistory}">
    <my:Properties>
      <my:Property my:Name="ColumnsToDisplay" my:Value="CreatedTime,Requestor,ComputedActor,Decision,Reason" />
      <my:Property my:Name="ResultObjectType" my:Value="ApprovalResponse"/>
      <my:Property my:Name="EmptyResultText" my:Value="There are no approval responses for this group." />
      <my:Property my:Name="PageSize" my:Value="7" />
      <my:Property my:Name="SearchControlAutoPostback" my:Value="false" />
      <my:Property my:Name="SearchOnLoad" my:Value="true" />
      <my:Property my:Name="ShowTitleBar" my:Value="true" />
      <my:Property my:Name="ShowActionBar" my:Value="false" />
      <my:Property my:Name="ShowPreview" my:Value="false" />
      <my:Property my:Name="ShowSearchControl" my:Value="false" />
      <my:Property my:Name="EnableSelection" my:Value="false" />
      <my:Property my:Name="SingleSelection" my:Value="false" />
      <my:Property my:Name="ItemClickBehavior" my:Value="ModelessDialog" />
      <my:Property my:Name="ListFilter" my:Value="/ApprovalResponse[Approval = /Approval[Request = /Request[Target='%ObjectID%']]]" />
    </my:Properties>
  </my:Control>
</my:Grouping>