Lazy Approval with SharePoint – approve task via email part 3


Part 1:

Part 2:

Building Visual Studio workflows can be a little intimidating at first, but the workflow we will be making in this post is very simple.  The purpose of this workflow is to take the emails as they come into the “Workflow Response” library we setup in step 1 and process them.  This means that the workflow will auto-start on item creation, look at the contents of the mail, get the subject like, parse the 4 variables out, and take action as needed.

Step 1:  Create a Visual Studio Sharepoint 2010 sequential workflow, and call it “ApproveViaEmailWF” or something appropriate.

Step 2:  Add a single “Code” action in the workflow immediately after workflow_activated.  In this example, i also put two LogtoHistory actions around the execute code activity, so i can see what happened on the workflow history page for this workflow item.  Basically, the first one says “Begin workflow”, and the second says “End workflow”.

Now let’s take a look at the code that parses the email:

Double-click the code activity, and you will see the execute code activity method.  You need to include in your code as well:

( this isn’t the cleanest code in the world, but it works.  update as you see fit. )


Imports System.ComponentModel

Imports System.ComponentModel.Design

Imports System.Drawing

Imports System.Workflow.ComponentModel.Compiler

Imports System.Workflow.ComponentModel.Serialization

Imports System.Workflow.ComponentModel

Imports System.Workflow.ComponentModel.Design

Imports System.Workflow.Runtime

Imports System.Workflow.Activities

Imports System.Workflow.Activities.Rules

Imports Microsoft.SharePoint.Workflow

Imports Microsoft.SharePoint.WorkflowActions

Imports System.Net.Mail

Imports System.IO

Public Class Workflow1

Inherits SequentialWorkflowActivity

Public workflowProperties As New SPWorkflowActivationProperties

Public HistoryDescription1 As System.String = Nothing

Public Counter1 As Integer

Public Sub New()



End Sub

Private Sub onWorkflowActivated1_Invoked(sender As System.Object, e As System.Workflow.Activities.ExternalDataEventArgs)

HistoryDescription1 = “STARTING WORKFLOW v2.  Pause for 5 minutes for workflow tasks to be unlocked.”

End Sub

Private Sub codeActivity1_ExecuteCode(sender As System.Object, e As System.EventArgs)

Dim obj As Object

Dim sSite As String = “”

Dim sList As String = “”

Dim file As SPFile = workflowProperties.Item.File

obj = workflowProperties.Item

Dim areader As StringReader = New System.IO.StringReader(file.ToString)

Dim breader As StreamReader = New StreamReader(Me.workflowProperties.Item.File.OpenBinaryStream)

Dim str As String = breader.ReadToEnd

Dim tempstr As String = “”

Dim listid As String = “”

Dim taskid As String = “”

Dim sEmailBody As String = “”

Dim pos As Integer = 0

Dim aitem As SPListItem


    Dim msgObj As New System.Net.Mail.MailMessage

Catch ex As Microsoft.SharePoint.SPException

End Try

   Dim testString As String = str

pos = InStr(str, vbCrLf & vbCrLf)


sEmailBody = Mid(str, pos, 100)

Catch ex As Microsoft.SharePoint.SPException

End Try

pos = InStr(str, “Subject: “)

If pos = 0 Then Exit Sub

tempstr = Mid(str, pos + 9, 120)

   Dim firstdash = InStr(tempstr, “+”)

   Dim UserAction = Left(tempstr, firstdash – 1)

tempstr = Right(tempstr, Len(tempstr) – firstdash)

firstdash = InStr(tempstr, “+”)

    Dim thisTask = Mid(tempstr, 3, firstdash – 3)

tempstr = Right(tempstr, Len(tempstr) – firstdash)

firstdash = InStr(tempstr, “+”)

sSite = GetSite(tempstr)

sList = GetList(tempstr)

   Dim gList As New Guid(sList)

sList = Replace(sList, ” “, “%20”)

    Dim site As New SPSite(sSite)

    Using web As SPWeb = site.OpenWeb()


        Dim alist As SPList = web.Lists(gList)

aitem = alist.GetItemById(CInt(thisTask))

    If UserAction = “APPROVE” Then

        Dim ht As New Hashtable()

ht(“Completed”) = “TRUE”

ht(“PercentComplete”) = 1

ht(“Status”) = “Completed”

ht(SPBuiltInFieldId.WorkflowOutcome) = “Approved”

ht(“Outcome”) = “Approved”

ht(“TaskStatus”) = “Approved”

ht(SPBuiltInFieldId.Description) = “Approved by email.  User comment: “ & sEmailBody

web.AllowUnsafeUpdates = True

        SPWorkflowTask.AlterTask(TryCast(aitem, SPListItem), ht, True)

ElseIf UserAction = “REJECT” Then

        Dim ht As New Hashtable()

ht(“Completed”) = “TRUE”

ht(“PercentComplete”) = 0

ht(“Status”) = “Completed”

ht(“Outcome”) = “Rejected”

ht(“TaskStatus”) = “Rejected”

ht(SPBuiltInFieldId.Description) = “Rejected by email.  User comment: “ & sEmailBody

ht(“FormData”) = SPWorkflowStatus.Completed

web.AllowUnsafeUpdates =True

        SPWorkflowTask.AlterTask(TryCast(aitem, SPListItem), ht, True)

End If

HistoryDescription1 = “END WORKFLOW.  id=” & thisTask & “: list=” & gList.ToString & “: site=” & sSite & “: action=” & UserAction

    Catch ex As Microsoft.SharePoint.SPException

HistoryDescription1 = ex.Message

       Dim ht As New Hashtable()

ht(“Completed”) = “FALSE”

ht(“PercentComplete”) = 0

ht(“Status”) = “Error”

ht(“Outcome”) = “Error”

ht(“TaskStatus”) = “Error”

ht(SPBuiltInFieldId.Description) = “Error: “ & ex.Message

ht(“FormData”) = SPWorkflowStatus.ErrorOccurred

SPWorkflowTask.AlterTask(TryCast(aitem, SPListItem), ht, True)

    End Try

End Using

End Sub

Private Function GetSite(fullString As String) As String

    Dim pos As Integer = InStr(fullString, “SITE”)

    Dim dsh As Integer = InStr(pos, fullString, “+”)

    Dim s As String = Mid(fullString, pos + 4, dsh – 5)

    Return s

End Function

Private Function GetList(fullString As String) As String

    Dim pos As Integer = InStr(fullString, “+LIST”)

    Dim tmp As String = Mid(fullString, pos + 5, 36)

Return tmp

End Function

End Class


THAT’S ABOUT IT!   In this code, I didn’t show some of my code, but there are functions to get the taskid, list, site, and action out of the email message.  Also, i am not showing a else clause that handles the REJECTION, but it is pretty much the same code, but sets the percent complete to 0, and the outcome and task status to “Rejected”.  I also do not show how i am setting the log to history, but this is easy…I dynamically set a field called HistoryDescription1 and the log actions simply show what i set it to. The line of code that updates the task is:

SPWorkflowTask.AlterTask(TryCast(aitem, SPListItem), ht, True)

Since this isn’t a post about how to develop in Sharepoint, I wont be covering how to setup a development environment, how to test, how to deploy a solution to the production server, or any of that jazz…perhaps that is for a different post.

If this compiles, then you are ready to test it.  Start an approval process on a fake document you put in that “shared documents” library.  Once you receive the email, click the link in it to ACCEPT and send the message.  It should make its way to the “workflow response” library.   From there, you can copy the eml file to your testing environment and work with it.

If all goes right, when you have deployed this to your production server, you should be able to attch the workflow to the “workflow response” library, and have it launch on new item creation.   when the email drops in the library, the workflow runs, updates the task, and exits.  If there was an error, the log will show the error message.  If it completes, then the log shows ‘END GETEMAILCONTENTS’.


Lazy Approval with SharePoint – approve task via email part 2

So at this point, you should have a document library that accepts emails.

We need to hop into Sharepoint Designer to set up a workflow that sends out an approval task to employees.  In this example, I have set up a workflow on my IT site in my site collection (this is where i play).  It is a very simple list workflow with one task in it….”Start Approval Process”.  NOTE:  this is only available in the enterprise license of Sharepoint.  You may create custom approvals in other versions of Sharepoint.  The important thing is that it is a task content type (either infopath or native task form).

For this excercise, I am using Sharepoint Enterprise, so the approval is a built-in template.  I created a workflow on the “Shared Documents” library.  I used the “Approval – Sharepoint 2010” template and called it “Testing”.  I set this as a manual start only, but you could set to start on item creation or change as well.

Next, I edit the workflow and add one task….”Start Approval Process”.  This allows me to specify a recipient for the approval, which in turn creates a task item in the task list specified in the workflow settings.  I also clicked the default workflow name and gave this task a unique name “DocApproval”.  This isn’t neccessary, but it looks better when the emails get sent out.

One more thing to note is the task list assigned to this workflow.  In this example, a list called “Tasks” is the list to which the workflow will put the approval tasks.  This name could be different or contain spaces.  Take note of the list name, though, as we will be extracting the list GUID.

In your workflow editor, you should see something like the image above.  I set myself as the approver, because it just makes it easier to debug.

Next, we need to dive into the approval step created by this template and edit the email that automatically gets sent out.  Click the “DocApproval” task (or whatever you called it), and you are brought to the customize task process page.

What we want to do here is set the email for the individual task processes created by this workflow, so we will click the link for “Change the behavior of a single task”.  You will then be brought to the “Task Behaviors” screen.  In the “When a task is pending” section of this screen, you will see a section of code that sends an email to the “Current Task:Assigned To”.  Click this link to get to the contents of the message that will be sent.

When clicking the link, you will see the “Define E-mail Message” dialog box:

I added only one line to this auto-generated email…”ACCEPT or REJECT via email”.  These are the links to allow a user on ANY DEVICE to approve through a regular email system.   How were these links created?  Let’s dive into the ACCEPT link to see how I constructed it.

When you click the edit hyperlink button on this dialog, you can set display text and the address.  I simply put APROVE for the display text, but the address is the tricky part.

CLICK the elipses next to the Address to edit the details:

Let’s take a look at what this link is going to do.  First, the MAILTO is an instruction to create a new email message.  All email clients know how to work with this, as it is a standard.  The first parameter is the recipient of the email (‘’).  Next, in querystring format, the subject.  The subject is the ONLY THING WE WILL USE, since the body of the e-mail can’t be set through this function.

The subject is going to contain 4 main peices of information: 1) the command, 2)the list id, 3) the task item id and 4) the site from which the approval came.   With these four peices of information, you pretty much have all the info you need to approve the task automatically.  This approval will be done with a Visual Studio workflow (part 3).

All of these “variables” we are putting in this link come from common lookups found in the Sharepoint Designer, such as [%current task: id%], [%workflow context: current site URL%], and others.  The command (first) could be anything you define.  I chose to use APPROVE and REJECT.

The one wierd thing here is that I am setting a GUID to the task list this is associated with.  The reason for this is the GUID will be more reliable than accessing the task list by name (like “Approval Tasks” or “Tasks”).   The GUID for the task list associated with your workflow can be found by going to your task list, click list settings, then go to target audience settings.  The GUID is in the URL for that setting.  Remove the brackets before adding to your modified task email.

Ultimately, the purpose of this whole email thing is so that when a user gets an approval task via email, they now can be on any device (like an iPhone), and simply click the link for approve or reject in the email and send send the email to approve.

Now that we have setup the library, the email settings, the approval process in sdesigner, we are ready to move on to the last step in this process…making the Visual Studio workflow and deploy it to the site.

Part 3:

Lazy Approval with SharePoint – approve task via email part 1

Don’t you wish that Sharepoint would give you a way to approve documents or tasks via email, and not have to login to a VPN to gain access to the corporate intranet site?  Wouldn’t it be nice if there was a way to just click a link in the email to approve or reject a task on any device?

This is what a “lazy approval” is.  Currently, i know of one company (as of the writing of this post) that has a lazy approval system as a Sharepoint add-in, and this one feature (packaged with many other Sharepoint improvements of course) costs $5,000.  Well, I didn’t need all these other features!  I just want to reply to an email and be done with it.

In order to make a lazy approval happen, there are going to be three main components to this:

  1. we will setup a library on the main sharepoint site to accept emails from the users
  2. we need an approval task in a workflow that will send the task notifications with specific formatting
  3. we need a workflow developed in Visual Studio 2010 that will parse through an email and approve for the user.

It doesn’t seem too hard, right?   In the following posts, we will setup each of these things, i will provide sample code, screenshots and the setup of the items to make this all work.

Setup a library to accept incoming emails

This is a very easy thing to do.  In sharepoint central admin, you must have incoming mail setup.  In the “system settings” section of central admin, you will click on the “Configure incoming e-mail settings” link.

NOTE: This article assumes you have already setup outgoing e-mail settings properly.

central administration - system settings

In this section, enable sites to receive email, and set all applicable SMTP settings.

system settings pageYour IT department or mail administrator should be able to help you get this setup properly.

Once you get this setup, then we need to move on to the library that will accept incoming emails.  I setup a library called “Workflow Response” at the top-level site as a document library and choose NONE as the document template.  I do not show this on the left-side navigation links, since people don’t need to see these emails as they come in.

Next, go to library settings and click the “Incoming e-mail settings”.incoming e-mail settings

You will be brought to the mail setup page for the library.  Make sure to select the option to “save original e-mail”!!!

Once you have setup the library to accept email, then test it by sending an e-mail message to the address you supplied for the library, in this case sharepointsite@server.domain.local.

Now we are ready to move on to the next part, setting up an approval workflow in Sharepoint Designer.


Part 2:

Part 3:

Squashing BUGS!


Welcome to real-world sharepoint!

Welcome to my Sharepoint blog.   My name is George Hardy, and i have been developing on the .NET platform for about 10 years.  I typically write database applications using, such as estimating systems, rental applications, equipment service applications and more.

I have recently gotten into Sharepoint at my job, and i would like to share the ups and downs of my experiences working with the system.