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.
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!
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:
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:
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.
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:
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:
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!
Thanks for the interest CQuick, I'm looking to get out the next installment by this week. It's probably the most in-depth article of the series, so it's been taking a while to write...
Great work with the articles Sean. It really helps us see the power of the applications and how to design our own for clients and internal use.
One thought, I know writing doc can take a long time, have you thought about producing screencasts instead of doc? This might help in speeding up the time it takes to produce how-to's on application template designs.
Great Examples. I used this example to create a Workflow with a Reminder and an OverDue on a Project List. The only question I have is when the Workflow is paused waiting for a reminder date what if I want to stop the workflow if someone changes the status of an Item to "Completed" it doesn't seem to be able to unPause to stop the workflow.
Great example to get started! I took your examples and be experimenting creating a shopping cart like application. The problem I’m having is getting input in a dataview. I understand how to put a label in a table and see it in workflow but, how a textbox? When I put a textbox in the table, I do not see it as a form variable to assign in workflow. Any thoughts?
I want to build a new web part but I don't know how SP store this information in database. I know that there're some tables like AllLists, AllWebParts...store information 'b lists, webparts... But I don't know where SP stored all items in these lists?
Please help me!
Its possible insert in the email a link to new item created in lists? how? (sorry for my english)
I created a subsite using WSS 3.0 template “Employee Training Scheduling and Materials”.
I gave some users Read permission and others Contribute permission.
Users with Read permission cannot do their registration for any course. (Registration Fails)
Users with Contribute permission can register fine, however they have more permissions that they should. For example, with Contribute permission they can add, edit or delete items, which they shouldn’t.
I found that this is a permission related issue.
My workaround was to give specific permissions for the courses list. I.e.:
-I created a new permission level from a copy of the Contribute permission level. I uncheck the “Add Items” and “Delete Items” permission and leaved only the “Edit Items” permission.
-Then on Courses permissions, I created a Group (named Training Users) and gave this group the permission level created.
With this I was able to give users the ability to register to courses and at the same time deny them the ability to add new items and to delete existing items.
Unfortunately this doesn’t answer to all my requirements because users can still edit items, i.e., they can change the name, time and available seats of any course.
Note: The “edit items” permission in the courses list is needed because each time a user registers for a course the “Attendee registration” workflow starts using the credentials of the user that initiated the workflow, and if this user only has Read Permission the workflow fails. The user needs to have the ability to “edit items” in the courses list because one of the steps of the “Attendee registration” workflow is the “enforce seating policy” which updates an item in the courses list.
In the registration list I gave the users the Contribute permission level because they need to add and delete items depending if they are registering or unregistering from a course.
Resuming, 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.
Can I run the workflows with elevated privileges independently of the users permission?
I don’t know if I made myself clear. Any question please feel free to contact-me.
Thanks in advance.
hey ...i used a workflow in my system..
but everytime the condition is true...it triggers the action more than once(around 10 times..)
please share your views on this..
Part 3? I know you're busy, but it would be great to see Part 3!
Need some help. I'm really interested in learning this but SPD gives me this ugly error:
Errors were found when compiling the workflow.The workflow files were saved but cannot be run.
Unexpected error on server associating the workflow
Everything out there on this error points to VS.NET developed workflows, not SPD. Help!?!
Well, I had the same error as David and my issue with SPD 2007. Going through the logs (%systemdrive%\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS) I found that the "temp" directory of the default NetworkService wasn't letting the SHAREPOINT\system user to compile the workflow. Once I modified the directory permissions to let SHAREPOINT\system compile in that location, the errors David and I see went away and SPD would also compile and associate a custom workflow.
Wow folks... thanks for all the comments! I'm sorry I haven't been around much as of late, we've all been busy planning out all the awesome new SharePoint Designer features you'll see in the next wave of Office. But now that I have some time to catch my breath...
Ben, CQuick: Part 3 is long overdue, I agree. Thanks for being patient - I hope you guys will find it useful in completing the picture here...
Scott: after the pause action, you can create a new step with a condition to check status before doing anything else. It's not a synchronous check though, for that I would suggest looking into using modification forms in the WinWF.
BBarthell: are you using custom form actions? You should be able to access any text box from the data form by pointing the lookup dialog (fx) to the "Form Fields" data source...
Hoalio: writing your own web part isn't in the scope of this post, but you may want to try the SharePoint SDK for information on how backend data storage is handled.
Pangolao: there isn't an easy way to do this today, but you can certainly handcode the path portion of the URL (http://server/site/List/DispForm.aspx?ID=) and then apply a lookup to the Current Item's ID, similarly to the examples in the post.
Goncalo: 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.
NehaShina: did you check the start trigger for the workflow? It's possible to get into this situation if you set the workflow to start every time an item is changed, and the workflow itself does the change (thereby triggering an action several times).
YokoGaijin: good catch and thanks for sharing.
Yes, I am using custom form actions ... my label shows up in the form fields (like your example) but, how can I put a text box in that is not related to the list? For example, I want to show a list of products to order and ask for a Qty for each product. The "Add to basket" is a custom form action (button).
Product A _______ "Add to basket"
Product B _______ "Add to basket"
Sean, one request. Can you tell me if the following even possible within the context of SPD (I know I can do it in VS... just didn't want to have to)?
I added another list populated by an InfoPath form. The "forced attendance" list allows HR and the supervisors in certain departments designate training as 'mandatory', 'highly suggested', and 'suggested'. The difference being that the first is required, the other two are courses that directly apply to goals & objectives or identified on the job training needs. The "forced attendance" list uses lookups from the "courses" list as defined in this example.
My conundrum is how I can insert registrations using a workflow in SPD. One of the fields in the "forced attendance" list is an attendees field populated with people picker and allows multiple entries. Is there a way I can do a "do while" or "for each" using SPD without creating custom workflow features or resorting to VS?
I surely adore the piece of work you are putting up here.
This has definitely served me as the starting point and now to run forward with the SPD flag.
3 Cheers to you!!
Keeping my fingers crossed till you get us the next installment :-).
Also, a small query...
I am using the "Do Calculation" Action in my workflow in SPD.
I am trying to use something like this:
Calculate FileNumber:OldFileNo plus 1 (Output to Variable:NewNo)
then use this variable "NewNo" to Update a column in myList which initially has a value "1".
When this workflow runs, I see that the status is completed and also that the
required list column is updated. But only problem is the updated value is incorrect.
Instead of incrementing the "1" to "2", I find a value "11" in that column.
This is wierd !!
The calculation can't be a concatenation. I need it to add 1 to the older value.
Am I missing on somethig.
Thanks for any kinda help.