Training Site Template - Part 2: Workflows

Training Site Template - Part 2: Workflows

Rate This

Sean Hey there,

 It's Sean again with another installment of the template development walkthrough. Last time we covered the list schema for the Employee Training Scheduling and Materials template, one of the new Application Templates for Windows SharePoint Services 3.0. In this series I'm showing you how we built one of these templates from the ground up, and today we're going to author all of the training site's workflows using SharePoint Designer. With that done, you'll have built the whole back-end of the template, so in a future installment we'll get our hands dirty with SharePoint's views and forms.

Folder List (Training Site)Open up a fresh installation of the training site in SPD and you'll see the folder list pictured on the right - with the three workflows you'll be creating today in the Workflow Designer. They'll provide some of the rich behavior that you wouldn't see in a static web app, and you won't need to write any code to get it done. Let's get started!

Automatic Reminders

People are forgetful. Computers aren't. So when an instructor comes to your training site to create a course, why should they have to remember when it is? That's where you can come in with this workflow, which we'll set on the Courses list:

  1. In SPD, go to File > New > Workflow...
  2. Name it "Instructor reminder" and attach it to the SharePoint list "Courses"
  3. Check the box to Automatically start this workflow when a new item is created
  4. Click Next > to continue

From here you'll want to create 3 steps which will logically separate our workflow into sets of related actions. Click Add workflow step to move on to the next step, and in each case if you don't see the specific action listed, click the Actions button > More Actions... to see the full list. Additionally I'm using [brackets] to refer to what you need to do in each placeholder.

Also, fx just means the data binding button. You get the same dialog whenever you push Add Lookup to Body in the e-mail builder.

So before you start, first create two variables (click Variables... > Add... to make them), one called "Reminder Time" (Date/Time) and one called "Subject" (String). You'll need them once you get to the individual steps:

  1. First step: name it "Set reminder time"
    1. No conditions
    2. Actions > Add Time to Date - Add -24 hours to [fx :: Current Item, Start Time] (Output to Variable: Reminder Time)
    3. Actions > Build Dynamic String - Store "RE: [fx :: Current Item, Course Title]" in Variable: Subject
  2. Second step: name it "Send confirmation"
    1. No conditions
    2. Actions > Send an Email - Email [...]
      1. To: Workflow Lookup... :: Current Item, Instructor
      2. Subject: fx :: Current Item, Course Title
      3. Body: This is a confirmation that you are scheduled to teach [Add Lookup to Body :: Current Item, Course Title] on [Add Lookup to Body :: Current Item, Start Time] at [Add Lookup to Body :: Current Item, Location]. For more information, please visit the training site.
  3. Third step: name it "Send reminder about course"
    1. Conditions > Compare Courses field - If Created is less than [fx :: Workflow Data, Variable: Reminder Time]
    2. Actions > Pause Until Date - Pause until [fx :: Workflow Data, Variable: Reminder Time]
    3. Actions > Send an Email - Email [...]
      1. To: same as above
      2. Subject: fx :: Workflow Data, Variable: Subject
      3. Body: This is a reminder that you are scheduled to teach [Add Lookup to Body :: Current Item, Course Title] on [Add Lookup to Body :: Current Item, Start Time] at [Add Lookup to Body :: Current Item, Location] in 24 hours. For more information, please visit the training site.
  4. Click Finish

Make sense? We're sending two reminders to anyone who adds a new item to the Courses list (i.e. an instructor creating a course) - one right when they make it as a confirmation, and another 24 hours before the course is scheduled. And the first step was just to keep the variable assignment separate, there's nothing wrong with doing it in the other steps right before they are used.

We'll want to do a similar reminder for students, but we can do it at the same time that we enforce seating policy (each item in Courses has an Available Seats and Total Seats) since it only really makes sense to send reminders if you actually make it into a course.

Processing Student Registrations

If you try to add a new course by browsing to Courses > New..., you'll see this line in the new form:

And you'll also notice that there's no line for Available Seats or Filled Seats, even though we added those fields to the Courses schema in part one. I'll teach you the trick to hide form items next time, what's important here now is that 0 will be our sentinel value in the workflow to come.

Let's go through this one a bit faster by using some shorthand.

  • ... means I'm referring to a string (probably for those long auto-generated e-mails) that you can find the full text for by opening Attendee registration.xoml in SPD.
  • Courses* means you need to perform a lookup back into the Courses list. To do this, start by pushing fx and pick Source: Courses, at which point you'll see the "Find the List Item" section appear. In that section, match Field: Courses:ID to Value: Registrations:Course ID (use the nearby fx :: Current Item, Course ID).
  • Variable: = anything with a leading Variable: in front of it is a workflow variable that can be found in fx :: Workflow Data.

Start with a new workflow called "Attendee registration," attached to Registrations and triggered when an item is created. Create 7 variables before you begin, 4 of type String (Subject, Confirmation Body, Reminder Body, Reminder Body 2), 2 Date/Time (Reminder Time, Reminder Time 2), and 1 Number (New Filled Seats). From there build out the steps:

  1. "Cache all variables"
    1. No conditions
    2. Actions > Build Dynamic String (x4)
      1. Store ... in Variable: Confirmation Body
      2. Store ... in Variable: Reminder Body
      3. Store ... in Variable: Reminder Body 2
      4. Store ... in Variable: Subject
  2. "Enforce seating policy"
    1. Conditions > Compare any data source (x2)
      1. If [fx :: Courses*, Total Seats] equals 0
      2. or [fx :: Courses*, Filled Seats] is less than [fx :: Courses*, Total Seats]
    2. Actions > Set Workflow Variable (x2)
      1. Set Variable: Reminder Time to [fx :: Courses*, Start Time]
      2. Set Variable: Reminder Time 2 to [fx :: Courses*, End Time]
    3. Actions > Do Calculation - Calculate [fx :: Courses*, Filled Seats] plus 1 (Output to Variable: New Filled Seats)
    4. Actions > Update List Item
      1. List: Courses*
      2. Add... :: Set Filled Seats to Variable: New Filled Seats
    5. Click Add 'Else If' Conditional Branch
    6. No conditions
    7. Actions > Delete Item - Delete item in Registrations (Current Item)
    8. Actions > Stop Workflow - Stop the workflow and log "Course is already full."
  3. "Set reminder time"
    1. No conditions
    2. Actions > Add Time to Date - Add -24 hours to Variable: Reminder Time (Output to Variable: Reminder Time)
  4. "Send confirmation"
    1. No conditions
    2. Actions > Send an Email - Email ...
  5. "Send reminder about course"
    1. Conditions > Compare Registrations field - If Created is less than Variable: Reminder Time
    2. Actions > Pause Until Date - Pause until Variable: Reminder Time
    3. Actions > Send an Email - Email ...
  6. "Send reminder for feedback"
    1. Conditions > Compare Registrations field - If Created is less than Variable: Reminder Time 2
    2. Actions > Pause Until Date - Pause until Variable: Reminder Time 2
    3. Actions > Send an Email - Email ...
  7. "Move to Past Registrations"
    1. No conditions
    2. Actions > Copy List Item - Copy item in Registrations (Current Item) to Past Registrations
    3. Actions > Delete Item - Delete item in Registrations (Current Item)

Yikes, I know. But thankfully that's about as bad as it gets. Here's a brief explanation - a student will register for a course, but they won't get in right away (technically, they get in but they may be kicked out immediately if the seating logic fails). After setting all the variables we check if Total Seats is 0 (unlimited) or greater than Filled Seats (i.e. is there room in the course?). If not, we abort prematurely (delete/stop step two), otherwise we do the math and add 1 to the Filled Seats, send reminders, etc. The last step is the archival in the mirror list Past Registrations, something that will become more evident in part three when we build up the front end.

A Dirty Workflow Secret

That's all well and good, but what happens when someone wants to unregister from the course? We need to subtract 1 from the number of Filled Seats, then delete their registration from Registrations, but here's a problem - workflow cannot be triggered to run before an item is deleted. So here's how we'll fake it - since workflow can run when an item is changed, we'll set up another sentinel value for our workflow to be interested in. DELETE seems pretty self explanatory, right?

(Note that this workaround has some serious implications - we'll need to hide item deletion in Registrations and block all edits to our field so the user never trips the sentinel. Don't worry, I'll explain in due time...)

Our last workflow, "Attendee unregistration" (for lack of a better word), attached to Registrations, triggered when an item is changed. One variable before you start: a String called Log Message. Not even really necessary, just for posterity:

  1. "Unregister from course"
    1. Conditions > Compare Registrations field - If Title equals "DELETE"
    2. Actions > Delete Item - Delete item in Registrations (Current Item)
    3. Actions > Build Dynamic String - Store ... in Variable: Log Message
    4. Actions > Stop Workflow - Stop the workflow and log Variable: Log Message

And there you have it, dynamic behavior that takes a long time to spell out in a blog post, but goes a long way towards the richness and robustness of your SharePoint app. It's taken us a few key design decisions to get here, and so in part three we'll learn what consequences that holds for the front end system, which is driven by SharePoint views and forms. I'll see you then!


  • I used the original template to design the course registration website. And I am having problems while deleting "My Registrations". On the home page, where "Courses I am attending" is displayed, when I click "Remove", it doesn't delete that particular registration. Instead it deletes the very first registration in the list. What I also observe is that the "Remove" link has the same parameter value (for the parameter ID) for all the registrations.

    Can someone please confirm if this is the case with them? And how to resolve this? I have recreated this site several times and I see the same issue all over.



  • MG: Add one line above the remove link in the XSL code, then change

    the 'remove' link parameter as follows:

    Add this line above the link:

    <xsl:variable name="CourseID" select="@ID"/>

    Change the parameter of the remove link (after the ID=) to read:

    {../../../Registrations/Rows//Row[@Course_x0020_ID=$CourseID and

    contains(@Author, $UserID)]/@ID}

  • Hello.

    I've changed the locale in the Regional settings of the site from English (US) to Dutch (Belgium). This causes an error when editing a course without changing the Start & End Time. Does anyone know how I can solve this?

    Thanks in advance.

  • Yannis: SUPER! That worked out just perfectly. Many thanks for your solution.


  • As a follow up to my earlier comment on the workflow for the "Attendee Unregistration" workflow, I've posted a walkthrough on how to adjust the workflow to increment the "Available Seats" variable after a user unregisters from a course.

    Hopefully this will help someone else if they have the same issue.

    And may want to look at modifying this post to include what I posted because you forgot to include this logic in your walkthrough.

    - Dink

  • Using Assign Form To Group Activity in SPD

    I have the workflow working where it creates a task for each user.  The problem is, how do I check what EACH user responded to and either approve or reject the workflow item?

    Thanks and fantastic job!

  • I have been working with this template and am ready for nearly for final user testing.  My last issue is identical to one posted earlier by Goncalo Feijao: "I want users to be able to register to courses but at the same time I don’t want them to add, delete or edit items in the courses list"


    You posted a response to this: "SPD's workflows always impersonate the person who initiated them, so no there isn't a way to elevate without developing your own non-declarative workflow."

    I saw no additional follow-up on this and am not sure how to interpret.  Can you elaborate a bit on this?



  • This is an awsome post!.    However, there has not been a response yet to Goncalo's post regarding setting permissions on the workflow so that a user does not need "Edit" permissions on the list to start the workflow.

    Ironically, I build a similar class registation, tracking system for our organization and discovered many of the same permission issues with running workflows that were identified here.     I have NOT found a work around for this either.  The difference here is that my workflow does NOT update the number of available seats.   Initially I had this feature but removed it since I thought it was the culprit but found that it does not matter.. to start the workflow on a list you need the Edit permission.

    To get around this... I changed the advanced settings on the list so that the item-level permissions are set to Edit Access = None.   This ensures that ONLY the folks who have "Manager List" privileges can edit the list items.   I'd rather give that permission to the 2-3 folks who add courses to the course calendar than the Edit permission to my 4000 other users.

    Regarding impersonation that Sean was refering to, this is true and is fundamentally how workflows work.  The only way to get around this is to program a custom workflow and use some Single-Sign On methods to imporsonate someone with the required permissions if that is required (eg.  Register for a class and log a custom audit row to a list that the user does not have access to add/view/or edit)

    A few lessons learned from my expeirence:

    1) I created a list that used to track the classes users registerd for on-line called My Classes.  Items in this List are created by the workflow and let our registrat keep track of who's registered for classes, approve their registrations and communicate to users if a class is cancelled.   There are even future plans to track attendance using this list.   The main reason I did this was because not all our users have access to EMAIL, some still use our Hospital Information System to send messages to each other.   So it was pointless notifying them by email to confirm they've been accepted into a class.   This way it make communication MUCH easier and will save countless hours of our registrar trying to track down folks if a class is cancelled or for some other reason.  

    2) I love the available seats feature and will likely bring this back into my list as I originally intended.  

    PS. Love the reminder time..  maybe we'll incopoate something like this into our list.  This is not possible with the alert features of SharePoint.  

    My only concern is that th OSTIMER service seems to have a memory leak making me weary of getting too deep into Workflows until that's resolved...  There is a hotfix out there from microsoft for that but we've not applied it yet.  A workaound is to restart the service every few days.



  • Anybody out there !!

    My "Copy List Item" action in the workflow has stopped working for no reasons I can think of.

    WIERD !!!!

    It was initially working but has started throwing an error "Error while copying...." kind of.

    Has anyone faced this problem.

    Need quick help!!!


  • Hello Sean,

      This is a very intesresting blog that i have read. I really appreciate your work and the patience that you had in explaining all the steps in creating the necessary lists and workflows.

      but, out there right now, I am waiting for your 3rd blog. I would be thankful if you could post the 3rd most wanted blog which as you said will explain the important steps to create a site template on our own.

      Could you please post the blog for all of us ?

    zillion thanks,

    -Kiran M

  • All there

    How can I customize this template so that it displays a message to user if he tries to create a course with duration longer than 2 hours and does not add that item.

    I created a workflow,  it deletes the item if duration > 7200, but the user has no clue of what has happened. Can I make it interactive and ask the user to change the timings ?

    ... Rocky

  • Rocky,

      You can do customization to your work by doing some Event Handling code work. There is lot of good stuff explained on how to handle events at: Check out the article for WSS -> Creating and Using Event Handlers in Windows SharePoint Services 3.0.

      Hope that helps.


    -Kiran M

  • Kiran,

    Thanks for the response. but i dont want to write managed code. I handled that with javascript code and that worked great.


    I have another problem. How can i determine if the user belongs to a particular sharepoint group, say MyCutsomGroup. Again, i want to do it without .Net code. Can this be done by editing some XSL/XML or writing Javascript ?


  • Hi all,

    I need ur help , i need to create workflow start automatically without add, edit or delete any item to my list

    bcz i need it work daily @8:00 am to send e-mail to end users.

    is it possible , plz tell me how i can do that????/


  • When the course is scheduled, the workflow is paused and waits for the reminder time. I'm doing a similar thing, but what happens if the date of the course is changed. The workflow doesn't updated itself and the old one is still in progress, which would send the reminder at the wrong time.


Page 4 of 7 (100 items) «23456»
Leave a Comment
  • Please add 5 and 1 and type the answer here:
  • Post