Ashley
Blog - Robert Bogue [MVP]
Rob's Notebook
SharePoint Calendar
Thor Projects LLC - Welcome : Blog - Robert Bogue [MVP]
Monday, February 22, 2010

The Public Debut of Super Pig

At the SharePoint Conference 2009 they were handing out flying pigs – including their capes. So my son and I developed a short story board, recruited a neighborhood friend and put together a little short movie staring Super Pig (the flying pig given away at the SharePoint Conference.) Take a look for yourself: http://www.thorprojects.com/Files/Movies/SuperPig/SuperPig.wmv

I've had this put together for a while – since everyone I've shown it to likes it so much I had to share it with the world.


1 Comment
 
Monday, February 22, 2010

Sending HTML Emails with System.Net.Mail.MailMessage is more than IsBodyHtml

It seems like lately I've been running into a series of things that should be easier than they're turning out to be. I'm not sure exactly why that is – but I've got another fun one to share.

So System.Net.Mail.MailMessage is the object (along with SmtpClient) which are used to send emails. There's a single property on MailMessage called IsBodyHtml that is supposed to indicate that the body of the HTML should be treated as Html. That's all fine and good except that my experience is that it doesn't work consistently. In fact, it rarely works for me. So how do you make it work? Well, you add Alternate views. MailMessage has a property AlternateViews which is a set of alternate views that can be displayed with the message. In order to get a HTML view I had to add a new AlternateView with the encoding of text/html or System.Net.Mime.MediaTypes.Text.Html. So when I want HTML I add this code to the bottom of the place where I'm sending the message:

AlternateView av = AlternateView.CreateAlternateViewFromString(msg.Body, new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Html));

av.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;

msg.AlternateViews.Add(av);

 

I also generally add one for plain text encoding for those email readers that can't cope with HTML which can be done by replacing the content type with 'Text/Plain' like this:

AlternateView av = AlternateView.CreateAlternateViewFromString(msg.Body, new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain));

av.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;

msg.AlternateViews.Add(av);

 

Hopefully this will make it easier to figure out what is wrong when you are trying to send HTML email messages and it isn't working.


Categories: Professional | 0 Comments
 
Monday, February 22, 2010

OnWorkflowItemChanged and Workflow Event Delivery problems

One of the problems that I recently ran across was that I had a workflow that would stop getting events delivered to it while it was running. It wasn't clear what was going on but with some help from Eilene Hao Klaka and Gabe Hall we were able to sort out the root issue. So let's take a rather simple workflow:

The key here is that the workflow watches the item using OnWorkflowItemChanged then stops watching the item while it works on watching some tasks and then it resumes looking at the workflow. This looks pretty harmless and it is – right up to the point where someone makes a change to the document when the workflow is looking for task updates – but not item updates. When this happen the workflow gets the event, can't process it – and ultimately it's no longer runnable. It won't respond to any more events or take any more actions. Making matters worse is that you don't so much as get a message in the ULS that this has happened.

So what's going on? The short of it is that the workflow host and workflow foundation believe that you need events on the item in the middle of this workflow because the scope of the correlation token for OnWorkflowItemChanged is the workflow itself. So even though there's not an activity that is listening – the WF host and foundation believe there should be. The first thought might be to change the scope of the correlation token for OnWorkflowItemChanged to something smaller – but that won't work because OnWorkflowItemChanged expects the token created at the scope of the workflow.

However, there is another approach that will work. You can setup a subscription for your own event by using the CallExternalMethodActivity, selecting the InterfaceType of Microsoft.SharePoint.Workflow.IListItemService and selecting the method name of InitializeForEvent. You'll also need to provide an ID (unique guid for the subscription), the itemId (which in our case we can get from workflowProperties.ItemId), and listId (again we can get this from workflowProperties.ListId). This sets up a subscription for events. The second activity we need is HandleExternalEventActivity. In this activity we select the same InterfaceType of Microsoft.SharePoint.Workflow.IListItemService and select an EventName of OnItemChanged. This will get signaled when the workflowItem is changed. The beauty of this is that you can place these two activities inside of a sequence activity and set the correlation token to the scope of the sequenceactivity. When the correlation token falls out of scope, the subscription for the events will automatically be removed.

I've bundled this into a custom sequence activity. The sequence activity that I've prepared will allow you to bind the SPWorkflowActivationProperties (workflowProperties) from which the activity will automatically get the listId and ItemId – or you can bind them individually. I've also allowed you to bind to the subscription id – while I don't know why you would need this, I was trying to be complete. Finally, I also added a Invoked method so you can run code based on the event happening. CAUTION: I'm not setting the sender when I call your method so you'll not want to use this for anything that requires the sender object. (i.e. for those cases when you need to know what branch you're in.) This is the same problem that OnWorkflowItemChanged/OnTaskItemChanged has – so I didn't see this as a big issue. The activity looks like this:

The code is available here – use it at your own risk.  All you need to do is replace your OnWorkflowItemChanged with this activity and the rest of your workflow should remain undisturbed.


Categories: Professional | 0 Comments
 
Sunday, February 07, 2010

OnTaskCreated, DelayActivity, and PersistOnClose – How you can force the creation of a task

I've been working on a rather complex SharePoint workflow and I've run into a few problems. The workflow does a parallel approval of a form – and well, I've discovered a few issues.

First, there aren't many examples of how to do parallel approvals. This is particularly true when you need to keep unique instance data per iteration of the replicator loop. However, by scoping the correlation token to the inner sequence activity in the replicator, using a custom sequence activity to hold the additional parameters you need, and using the ChildInitialized event of the replicator that can be done.

However, I also ran into some odd problems that only occur when a workflow is doing parallel execution. But before I get there, I have to explain how CreateTask works. The CreateTask activity doesn't actually create a task immediately, it creates a request to create the task when the workflow is serialized. The basic thing is that they want to minimize disk IO on the SQL server so if you wanted to change things after creating it – but before it's written to disk in the list – you could. However, there are many situations where you need to force the task to be created so you can start to use it in your loop conditions. Commonly you want to know if the item exists and isn't completed. If you put this at the top of a while loop immediately after a CreateTask you'll never enter the while loop because the task will be missing from the point of view of the condition.

To every problem there is a solution, enter the OnTaskCreated activity. But wait, I have to mention that Microsoft is recommending that you not use OnTaskCreated – if you don't believe me check out KB 970548. They say to use DelayActivity. I've commened on DelayActivity in the past, particularly about the fact that you can't have a DelayActivity that runs for less than a minute. Well, I was wrong. There's a situation where you can have a DelayActivity fire in less than a minute.

If you're running a parallel situation (say inside of a replicator) and you hit a DelayActivity the wait is put on a timer queue inside of the workflow – just like any other workflow foundation workflow. The other branches will continue to run while the DelayActivity quietly ticks off the time. So it is technically possible to have DelayActivity sit for less than a minute.

In my case, I had set my DelayActivity to one second and didn't think anything about it. That is until I got a Null reference exception thrown back at me from the workflow. Why? Well, it seems like in the serialization process for the workflow the event fired and well, the SharePoint workflow host didn't know what to do with it. (Reportedly this is fixed in SharePoint 2010 but I've not tested this.) So now what do I do?

Well, enter the PersistOnClose attribute. This is an attribute in the Workflow Foundation that signals to WF that the workflow should be serialized immediately after completing the activity. This sounds pretty good… I can do a create task then an activity that has this attribute and all is well. Of course, if I had 100s of parallel branches in a workflow this would be sort of abusive on the system forcing it to serialize a workflow 100s of times – but for my case where I'm only ever a dozen or so branches wide at the same time it works fine.

All I did was I created a new activity that does nothing – except it has a PersistOnClose attribute on it. I put this immediately behind my CreateTask and voila. I get my task created. There's no crazy eventing going on. There's no delay while the system goes to sleep and wakes back up – just a little extra overhead on the system.

Problem solved. It's more than a bit crazy how you solve something like this – but it works and I've got one less problem to worry about.

[Update: You can download a copy of my code (use at your own risk) here.]


Categories: Professional | 3 Comments
 
Tuesday, February 02, 2010

SharePoint Saturday Wrap Up and Thank You

Last Saturday, January 30th 2010, we did a SharePoint Saturday event here in Indianapolis. From the perspective of most folks it was a roaring success. Kevin Dostalek posted his recap already. David Petersen posted a few pictures before we got rolling. Woody Windischman, Chris Geier posted about their experiences and both Enrique Lima and Rob Wilson posted their slide decks.

For me I felt like I was in the center of the storm. Officially the SharePoint Users Group of Indiana executed the event. (i.e. the users group held the money) However, I can honestly say that my introduction to the steering committee was correct – I didn't really feel like I did that much. In my conversations with the other members of the steering committee they felt largely the same way. That – to me – says a lot about the group of folks committed to making the event a success. We all compete for business in Indianapolis – and at the same time we worked together to deliver a community event that most people walked away from happy.

I wanted to say thank you again to the sponsors: Ambassador, Apparatus, CDW, Idera, K2, Microsoft, SHI, Wrox. Without their generous support the event simply wouldn't have been possible.

I also wanted to thank everyone for coming. A SharePoint event in Indianapolis with 372 registrations and 250 attendees should demonstrate what a great community we have in Indianapolis.


Categories: Professional | 1 Comment
 
Tuesday, February 02, 2010

WhitePaper: Customizing the Content Query Web Part in SharePoint Server 2007

The powerhouse of SharePoint publishing sites is the ContentByQuery web part – more affectionately known as the Content Query Web Part or CQWP. Last year I wrote a comprehensive article on customizing it that was published today at http://msdn.microsoft.com/en-us/library/ff380147.aspx Check it out and let me know what you think about it.


Categories: Professional, Articles | 0 Comments