Monday, 31 March 2008

This blog is changing URL..

..but I'm hoping you won't notice! The new URL is the somewhat simpler www.sharepointnutsandbolts.com, and if everything goes to plan everything will be redirected from old to new, so any bookmarks should continue to work. Or that's the theory promised to me by my blog host, but I thought I should say something just in case :-) Certainly all subscribers to the feed won't need to do anything - the Feedburner URL will stay the same.

Changeover will be later this week - if anybody notices a problem I'm keen to hear, please drop me a comment to let me know!

P.S. I'm likely to switch over to using the CKS : EBE framework at some point soon. A few people have commented that since we moved into 2008, they found it harder to find articles since the navigation on the right buries them under the '2007' node. I've tried to mitigate this by adding the 'Most popular articles' list (as indicated by Feedburner), but I know navigation isn't ideal. Rest assured I'll improve it when I have more control over the site!

Sunday, 16 March 2008

Great controls to be aware of when building SharePoint sites

Something I've been meaning to do for a while is discuss some of the controls I've found useful when putting together SharePoint sites. Obviously before building a custom control for a specific behaviour, it's a good idea to check if SharePoint comes with anything that will do the job. It sometimes surprising what you find! Certainly those from a Content Management Server background will be familiar with the idea of having lots of reusable controls (which used the CMS API) within the team, but in MOSS some of the equivalent controls come for free. The most obvious is the Content by Query web part (though we won't mention that the output HTML isn't accessible [WCAG AA-compliant] without extra work), but some of the smaller controls deserve some attention too. So here's a rundown of some handy items for the toolbox:

SPSecurityTrimmedControl

This control can be used to selectively display content or controls depending on the current user's SharePoint permissions. Whatever is the inner content of the control will therefore not be shown if the user doesn't have the specified permissions. An example would be:

   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" Permissions="ManageWeb">
   2:     This is only visible to users who can manage current web..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

In addition to the Permissions property shown above (enum), the PermissionsString property can be used to specify a comma-separated list of required permissions. This can be used in conjunction with the PermissionsMode property which takes 'All' or 'Any', and also the PermissionContext property which specifies what SharePoint object (e.g. 'CurrentList', 'CurrentFolder', 'CurrentItem', 'CurrentSite', 'RootSite') the specified permission applies to, so it's flexible. Examples of the permissions which can be specified include 'ManageLists', 'AddListItems', 'ViewPages', 'ManagePermissions' and so on. These are members of the SPBasePermission enum and Zac Smith has a full list. Notably many of these permissions are probably more useful in a collab scenario rather than WCM. [UPDATE: Text in this section updated following comment below/further testing:] For WCM folks the control does look like it does something useful in being able to filter on authentication type - unfortunately this doesn't work as advertised. For reference (in case MS fix it), the usage is:


   1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
   2:     This *should* be visible only to anonymous users but it doesn't work..    
   3: </SharepointWebControls:SPSecurityTrimmedControl> 

As you'd expect, valid values here are 'AnonymousUsersOnly', 'AuthenticatedUsersOnly' and 'AllUsers' - however as noted in the comments below 'AuthenticatedUsersOnly' is the only flag which seems to work properly.

There are some other interesting properties too:

  • PagesMode - valid options are 'Design', 'All' and 'Normal'

  • QueryStringParametersToInclude

  • RequiredFeatures

These look like extra properties to filter on, but alas I couldn't get these to work either. My investigative powers let me down here as firing up Reflector onto both SPSecurityTrimmedControl and the related RightsSensitiveVisibilityHelper class didn't give anything away in terms of usage, nor did Google or searching the 12 hive. Certainly it looks like specifying values is all that would be needed for latter two parameters, but I had no joy. A final consideration for SPSecurityTrimmedControl however, is the idea of further possibilities opened by deriving from this control in the same way SPLinkButton does.

EditModePanel

Where the previous control examined the user's permissions to establish whether content should be shown, the EditModePanel looks at whether the current page is in display or edit mode. This can be incredibly useful in the WCM world for displaying help messages or other content to users as they edit a page. However there are other uses - hiding navigation, adding inline CSS override classes to use different formatting (particularly useful) and emitting debug information in the HTML output are all examples. The declaration for this control would look like:

   1: <PublishingWebControls:EditModePanel SuppressTag="false" GroupingText="Title help" 
   2:         PageDisplayMode="Edit" runat="server" id="EditModePanel1">
   3:     Page titles should be concise
   4: </PublishingWebControls:EditModePanel>

At run time in edit mode, this would look like:


EditModePanel

A prettier usage might be to include the field controls within each respective EditModelPanel, meaning the input control is shown within the borders of the box. To do this we'd also need an extra EditModePanel set to PageDisplayMode="Display" which also contained the field control, since the original will display in edit mode only. Note this control will output a surrounding <div> unless the SuppressTag property is set to 'True'.

ListItemProperty

One control you might use in conjunction with the previous control is the ListItemProperty control. This simple little fella allows you to write out a particular field's value for a list item. The field which is rendered is specified with the Property attribute, where we specify the field's internal name. By default, the current list item (i.e. for the page we're viewing) is used, but this can be overridden by specifying the List property - to do this we specify the list GUID with braces:



   1: <SharePointWebControls:ListItemProperty runat="server" id="ListItemProperty1" 
   2:     List="{C110296D-2BE9-4818-80EE-A06BD018DE2F}" ListItemID="1" Property="Title"/>


I think of this control as doing a .ToString() on the contents of the chosen field. This can sometimes be useful in WCM where you want the value to appear in a different location onthe page to where it is edited - for example within a <title> tag. Note we can also specify the version of the list item we want to retrieve the value for with the ListItemVersion property.

ListProperty

Much the same as ListItemProperty except on a list itself. These two may well be used together as the code in a list's DispForm.aspx does:



   1: <SharePoint:ListProperty Property="LinkTitle" runat="server" id="ID_LinkTitle"/>
   2: : 
   3: <SharePoint:ListItemProperty id="ID_ItemProperty" MaxLength="40" runat="server"/>

This will show the current list's title, followed by a colon, followed by the current list item's title.


ProjectProperty


Slightly confusing name, but this control allows us to write out some properties of the current site/web. The MSDN documentation shows the valid list to be:

  • BlogCategoryTitle - Category of the current post item

  • BlogPostTitle - Title of the current post item

  • Description - Description of the current Web site

  • RecycleBinEnabled - 1 if the recycle bin is enabled; otherwise, 0

  • SiteOwnerName - User name of the owner for the current site collection

  • SiteUrl - Full URL of the current site collection

  • Title - Title of the current Web site

  • Url - Full URL of the current Web site
So the following would write out the title of the current web:



   1: <SharePointWebControls:ProjectProperty runat="server" id="ProjectProperty1" 
   2:     Property="Title" />

DelegateControl

This control provides an architecture where an 'outer' control is added to the page layout/master page, but the 'inner' control is determined by a Feature, thus allowing the control to be swapped out without having to modify the original template. This is used widely in Microsoft's master pages. This is a great control, I discuss it more fully in my Using the DelegateControl post.

AssetUrlSelector

To finish on something slightly different, this control can be invaluable when developing custom web parts and field controls. It provides the 'file picker' experience authors are used to when working with SharePoint, and fits well when you have a control which requires a file to be selected. So in several of my controls I allow the user to override the XSL file used for formatting, and the AssetUrlSelector is added to the user control I use for the edit view of the control. This provides me with the 'browse' button which will launch the picker when clicked:


AssetBrowseButton 
Clicking the button then shows:

AssetUrlSelector 
The AssetUrlSelector provides a fairly good user experience, including opening the picker in the location of the currently-selected file if one exists, or defaulting to a specified default location if not. Along similar lines if you need to provide a slick experience to select an image is the RichImageSelector - this is the picker used by the RichImageField control.

Summary

So that's some of the controls I've found useful. Obviously there are stacks more and the ones I did highlight can be used in lots of interesting ways which aren't discussed here. But hopefully this does serve to remind you to check what's in the box before spending time writing your own!

Thursday, 28 February 2008

New version of Content Deployment Wizard released

I'm happy to announce that development/testing of the next version of the SharePoint Content Deployment Wizard is now complete, and the new release has been uploaded to Codeplex. If you're not aware of the tool then Introducing the SharePoint Content Deployment Wizard is a good place to start. I'm calling this release "beta 2", which indicates the tool is by no means complete, but is significantly more mature than the original version. I'd encourage all of the 1400+ people (and counting) who downloaded the original in the past few weeks to get the latest version - the link is at the bottom of this post.

So let's run through the improvements:

  • Better support for large sites:

    The treeview of the site is no longer built in one hit - instead, portions of the site are retrieved as webs/lists are expanded. This can make a huge difference when working with large sites.

  • Support for WSS-only sites:

    The dependency on the MOSS assembly (Microsoft.SharePoint.Publishing) has now been removed, so imports/exports to WSS sites are now possible. The MOSS-specific functionality is only used if we are indeed in a MOSS environment - if you're interested, this is just related to honoring any publishing schedules (i.e. page expiry) from the source site on the target site.

  • Everything in one .exe:

    No separate WizardBase.dll any more.

  • Easier access to log files after import/export:

    Since it's highly important to analyze the log file for errors/warnings after an import/export, these now open automatically in notepad at the end of the operation. There's also a link in the tool to open the log file, so no more manual digging around the filesystem.

  • Auto-discovery of sites in 'Site URL' box:

    No need to re-enter the site URL each time - the sites available for selection in your SharePoint environment are automatically shown in the auto-complete entries for the textbox:

     AutoSiteDiscovery

  • Pretty icons in treeview :-)

    Easier visual distinction between objects in the tree:
     TreeViewImages

  • Logging via System.Diagnostics.Trace:

    In addition to the log file generated by the actual import/export operation, general operations in the tool are now also logged (separately) via System.Diagnostics. So if you have a problem using the tool, it should be easier to pinpoint the actual error - enable by adding a .config file with a TraceSwitch entry for "COB.SharePoint.Utilities.ContentDeploymentWizard" and appropriate TraceListener entries.


  • More reliability:

    Miscellaneous minor bug fixes, including removal of the somewhat psychedelic flickering as the confirmation screen is built.

In terms of issues reported in the first release, it seems that 95% of them have been related to the underlying Content Migration API provided by SharePoint rather than the actual Wizard - this means the issue would also likely surface if out-of-the-box Content Deployment (via Central Admin) or STSADM -export was used to deploy the content instead. This is in line with what I was expecting since in many ways the Wizard doesn't actually do that much - it just presents a nice UI (with the ability to cherry-pick exactly which items should be deployed) onto existing SharePoint code. Since there are some gotchas with the MS bits however, it does mean that understanding how their API works can help when using the Wizard. My recommended reading list for those wanting to know more would be:

The future

I've had great feedback so far (ranging from feature requests to "why the hell don't you charge for this thing?!" to offers of collaboration). In terms of features, it looks like incremental deployment is the most popular vote so far so I'm hoping to add this fairly soon. Another possibility is making the Wizard scriptable by refactoring the code into something more tiered, thus providing features like the ability to save configurations for import/export jobs and also allowing them to be hooked up to a task scheduler in some way. 

However, the more feedback the better and if you'd like to see particular functionality or have run into problems trying to use the tool I'm definitely keen to hear. Either leave a comment on the blog or over at the Codeplex site.

The download link
 

Go to www.codeplex.com/SPDeploymentWizard and go to the releases tab to download the latest version. Hope you find it useful.

Wednesday, 20 February 2008

More workflow issues and resolutions

What I wanted to do today is run through is wrap up my series of workflow posts, by running through some of the issues I hit during development. If you're into workflow, you may have seen Rob Bogue's excellent 10 issues (and resolutions) for SharePoint+Workflow - this post is something of an extension to Rob's. It probably makes for rather dry reading, but will hopefully be useful to those Googling for specific problems.

  1. Issue:- Error message "System.Xml.XmlAttribute is not serializable" shown in workflow history list and workflow will complete before it is supposed to. In SharePoint log, will see "End of Stream encountered before parsing was completed” error.

    More information:- This is caused by trying to store a class which cannot be serialized as a member (e.g. private variable) of the workflow class. Furthermore, I also encountered this error when storing the class which represents the InfoPath form - interestingly this class will always definitely be serializable, but the error was happening nevertheless.

    Resolution:- Mark member as NonSerializable (so workflow does not attempt to serialize the class, and copy primitive data (e.g. strings, dates etc.) out of form class and into individual field-level variables. It might also be possible to mark the ‘AnyAttr’ member of the generated class as NonSerializable instead.

  2. Issue:- InfoPath forms do not get updated upon solution deployment - changes are not reflected.

    More information:- This happened when I initially started development. I noticed that if I separately deactivated/uninstalled the workflow Feature, the InfoPath forms would then update successfully. This pointed me to an issue with the PostBuildActions.bat script.

    Resolution:- Modify PostBuildActions.bat to use the -force parameter when deactivating/uninstalling the Feature.

  3. Issue:- Error message "The e-mail message cannot be sent. Make sure the outgoing e-mail settings for the server are configured correctly." is shown in the workflow history list. E-mail configuration is known to be good (e.g. alerts are working).

    More information:- I suspect there are several reasons why this may happen, but for me the problem was how I was using the SendEmail activity. When I was setting properties on the SendEmail variable the workflow designer had added to my class for me, I had the error.

    Resolution:- The best way I found to deal with this was to set properties on the object which is passed to the SendEmail's 'MethodInvoking' code. You'll need to cast to SendEmail before you can do this.

  4. Issue:- The TaskID for a CreateTask activity does not get populated. The value defaults to -1 instead.

    More information:- This will be an issue in several scenarios, one example is when building the URL for a task (e.g. to include in an e-mail to a user) with code similar to Paul Hunt's code shown at http://suguk.org/forums/thread/4978.aspx.

    Resolution:- The TaskID will only be populated if there is a place to store the value. Ensure the TaskID property on your CreateTask activity is bound to a variable.

  5. Issue:- Error message "Correlation value on declaration 'x' is already initialized" message shown in SharePoint log when a task executes for the second time in a workflow.

    More information:- This scenario can happen when a CreateTask runs for a 2nd time due to an approver rejecting something in the workflow.

    Resolution:- The 'OwnerActivtyName' of the task's correlation token is incorrectly set. In a state-machine workflow, it should refer to the particular state, not the overall workflow.

  6. Issue:- Unexplained errors when some part of an otherwise functioning workflow is commented out in the designer (e.g. 'Failed on start').

    More information:- You've commented out one or more activities in your workflow (using right-click > Disable), but observe strange behaviour and/or errors. This is actually because, in contrast to commenting out source code, the code is actually still compiled into the assembly. Hence if there is an error with say, a correlation token being incorrectly set, this will still cause a problem in your workflow.

    Resolution:- Fix problem or remove faulting activity/activities completely from workflow.

For completeness, some other common issues which are well documented in other sources are:

  • 'Failed on start' - this often caused by a problem loading the workflow assembly. Check the PostBuildActions.bat script (or whatever you are using) has properly deployed the assembly to the GAC, and all the assembly attributes (version, public key token etc.) match those specifed in the Feature files.
  • 'SPException: This task is currently locked by a running workflow and cannot be edited' message. This is documented by Rob Bogue and others, but I'd also add this can occur as a knock-on effect of another problem. If an earlier exception has occurred whilst trying to process a task, the workflow will have locked the task on the first run, meaning that subsequent attempts by a user to respond to the task will result in this error. Other than fixing the problem at source, I haven't found an elegant way of dealing with this.

So hopefully that's of some use to workflow developers. If you haven't seen it already, there's a lot more goodness (sample code, tips and tricks etc.) in my workflow resources pack from the workflow deep-dive presentation I did at the UK SharePoint user group earlier this year.

Happy coding!

Sunday, 3 February 2008

InvokeWorkflow - child workflows not supported in SharePoint?

Our client through an interesting curve ball at us midway through my workflow project. We were probably 70% code complete, when it was realised the design had no provision for a certain scenario. The workflow is triggered by a user completing an InfoPath form - this has many input controls to collect data, and several of them are dropdowns which dictate the particular path through the workflow which occurs. One such dropdown looks something like:

Type:

  • type 1
  • type 2
  • both

The value selected is evaluated by IfElse conditions in the workflow - if "type 1" is chosen, we go down a particular branch, if "type 2", a different branch and so on. Now for this particular dropdown, we realised we hadn't specified what should happen for the "both" condition (that's agile for you!). The new requirement which arose was that notifications and tasks should go to to two teams (i.e. both of them), and after further discussion we decided it would be appropriate to kick off two separate workflows, one for each team.

Most workflow frameworks can deal with this by using 'child' workflows, and Workflow Foundation is no different. After a couple of hours refactoring the implementation to use this approach, my proof-of-concept looked like:

ParentWorkflow

So whilst the 'InvokeWorkflow' activities are closed (i.e. the child workflows aren't expanded), you can hopefully see that if we go down the branch on the left, we launch one child workflow, but in the branch on the right (for "both"), we launch two child workflows in parallel.

The problem

I tried a couple of things, the image above shows using a sequential parent workflow (though the child workflow is state-machine), but couldn't get this to work. Using the debugger I could see that the child workflows were being initialized - I could step through InitializeComponent(), but breakpoints in 'OnWorkflowActivated' were never hit - the event wasn't raised. The SharePoint log showed:

System.Workflow.Activities.EventDeliveryFailedException: Event "OnWorkflowActivated" on interface type "Microsoft.SharePoint.Workflow.ISharePointService" for instance id "67c4929a-9280-4b8b-9a4c-a1b85b04267c" cannot be delivered. ---> System.InvalidOperationException: Event Queue operation failed with MessageQueueErrorCode QueueNotFound for queue 'Message Properties Interface Type:Microsoft.SharePoint.Workflow.ISharePointService Method Name:OnWorkflowActivated

So I became suspicious of whether it's possible to use InvokeWorkflow in a SharePoint workflow. Microsoft haven't yet got back to me on this, and the most detailed SharePoint workflow book I'm aware of (Workflow in the 2007 Microsoft Office System by David Mann) does not discuss InvokeWorkflow.

Time for some lateral thinking.

The solution

We know that we will definitely have two workflows if two completed InfoPath forms are added to the document library, so I asked the client what they thought of getting the system to split a "both" request into two separate items - this would mean having two separate InfoPath forms in the list. The answer was "actually that's probably better for the users come to think of it", so the solution I came up with was:

  • a list event receiver which executes before the workflow - this does the following:
    - opens the binary contents of the InfoPath form as a string, and modifies the dropdown value from "both" to "type 1"
    - creates a duplicate of the original file in the same library, but modifies the dropdown value here from "both" to "type 2"
  • running some ad-hoc code to reorder the event receivers on the list - we need to split the item before workflow starts (so we get two workflows) but SharePoint adds the SPWorkflowAutostartEventReceiver at sequence of 1 so it executes first. This code switches the event receiver sequence so our pre-processing happens first.

On the last point, I considered that the SharePoint team may have set workflow to execute first for a reason, but then again it's just another event receiver and SharePoint does not prevent this reordering (in the same way it does with some other changes, such as deleting the welcome page from a web for example). In any case, we've tested pretty extensively and the solution hasn't missed a beat.

In case it's useful, the following code can be used to duplicate and modify a file - this could be used in a list receiver (as in my case) or in ad-hoc code. I used simple string manipulation to perform the modification since InfoPath forms are just XML, and also because using XML-handling methods caused issues with the 'proprietary' InfoPath declarations. Simply replace the string replacement line with your own processing.

   1: private string getFileContentsAsString(SPFile file)
   2: {
   3:     byte[] aFileBytes = file.OpenBinary();
   4:     string sContents = Encoding.UTF8.GetString(aFileBytes);
   5:  
   6:     return sContents;
   7: }
   8:  
   9: private void duplicateFile(SPListItem originalListItem, string sNewItemUrl, 
  10:     bool bDeleteOriginal)
  11: {
  12:     SPFile originalFile = originalListItem.File;
  13:     string sOriginalFileContents = getFileContentsAsString(originalFile);
  14:  
  15:     // TODO: replace this line with your own processing..
  16:     string sNewFileContents = sOriginalFileContents.Replace("oldValue", "newValue");
  17:     
  18:     byte[] aNewFileBytes = Encoding.UTF8.GetBytes(sNewFileContents);
  19:  
  20:     // we effectively need to add a new file for this operation, and optionally delete 
  21:     // the original..
  22:     SPFile newFile = originalListItem.ParentList.RootFolder.Files.Add(sNewItemUrl, aNewFileBytes,
  23:                                                                       originalFile.Properties);
  24:     newFile.Update();
  25:     
  26:     if (bDeleteOriginal)
  27:     {
  28:         originalListItem.Delete();
  29:     }
  30: }



P.S. If anyone is using InvokeWorkflow in a SharePoint workflow, leave a comment :-)

Sunday, 20 January 2008

Workflow: tasks which can only be actioned by task owner

When coming to Visual Studio workflows, something which surprises many SharePoint developers is that tasks assigned in the workflow can actually be actioned by any SharePoint user with basic permissions. In the worst scenario, 'basic permissions' means any user with contribute permissions to the SharePoint web which contains the workflow tasks list, and clearly this could be a whole lot of users who have nothing to do with your workflows. In the best case, you might have tied down permissions so that only users involved in workflows can use the list. Even so, this still means that any actor in the workflow can respond to any task, not just tasks which have actually been assigned to them. To my mind, this is bad - all it takes is a confused user to accidentally respond to someone else's task and your workflow is in a whole world of chaos.

So what can we do about this?

Well, there doesn't seem to be much written about this, but fortunately the best solution (AFAIK) is also the simplest one. Before we dive in, I notice other people needing to solve this problem have taken the approach that since a workflow task is just a list item, we can execute some code to set permissions on the list item using the API. A logical tactic, but happily there is special provision for doing this in the workflow framework - we still need to write a little code, but it's much simpler than that approach. The key is the 'SpecialPermissions' property of the CreateTask activity:



Pitfall - confusingly, clicking the ellipses button (...) for the property presents a generic VS collection editor (shown below), which as far as I can tell just flat cannot be used with this property - all the controls are disabled!




I'm assuming this is a bug in the Visual Studio 2005 Extensions for Workflow Foundation, so we'll ignore that! However, clicking the tiny blue 'bind property' button presents the more familiar 'bind the property to an instance variable' dialog - assuming you haven't already created a variable to store the permissions for this CreateTask, we should select 'Bind to a new member', and create either a field or property to store the permissions:



This creates a collection object, specifically a HybridDictionary, to which we can add items for each permission we need for this task. And we only need a handful of code lines to do it! Since we're likely to use it for many (i.e. all) tasks in our workflow, let's have a separate method we can call each time:

private void setTaskPermissions(HybridDictionary boundDictionary, string sTaskOwner)

{

     boundDictionary.Clear();

     boundDictionary.Add(sTaskOwner, SPRoleType.Contributor);

     boundDictionary.Add("NT AUTHORITY\\authenticated users", SPRoleType.Reader);

}


So, we pass in the collection specific to each task, and also the string username for the task owner. We then add an entry for the task owner to the dictionary with the 'contributor' permission, and one for all other users with just read permissions. Note we also clear out the dictionary before adding in case this task has already been issued (i.e. something got rejected in the workflow and we came back to this task a second time) - this avoids any errors due to the key already existing in the dictionary.


The calling code then, looks like this:

setTaskPermissions(approveExpenseClaim_SpecialPermissions, taskProps.AssignedTo);

This would be added to the code for each CreateTask activity in your workflow. The first parameter is the variable we bound earlier to the SpecialPermissions property (of the particular task we are dealing with), and taskProps is the SPWorkflowTaskProperties object which holds data for the task.

And that's it - much less code than you'd need to modify permissions for the list item with general API usage. The effect of this is that the task owner is the only standard user (administrators with full control excepted) who can respond to the task, but all others can read it. Needless to say, you could customize the code to your specific permission requirements if they are different to mine.

The user experience

One final thing worth pointing out is that the user experience might not be quite as slick as you'd like. Since we've restricted permissions on the item, any user who clicks on the task but doesn't have permissions will see the standard access denied message:



Personally I think an improvement would be to show a more friendly message, but this would require substantially more effort and complexity. My view is that for a few lines of code, this approach is a great trade off between effort required and benefit of protecting the integrity of the workflow - I'm definitely not a fan of sleepless nights wondering just what would happen in the workflow if users unintentionally responded to tasks which didn't belong to them, so it works for me. As always, if you've implemented a different way of dealing with this problem or have other comments, it would be great to hear.

Thursday, 10 January 2008

Resources from my workflow deep-dive presentation

Had a lot of fun presenting VS workflows at the UK SharePoint user group last night. We seemed to have weather issues (again) which might have put some people off travelling, but we still had a great crowd of around 100-120 people. As promised, here are my resources (my 'workflow bonanza pack' in fact ;-)). In the zip behind the link below, you will find:

  • Slide deck (presentation title: "Workflow: A deep dive into developing workflows for SharePoint using Visual Studio and Infopath")
  • Sample code for the state machine expenses workflow I built
  • My workflow information pack document

Much of the goodness is in the document - the contents are:

  • Links to my favourite workflow resources (blogs, books etc.)
  • How to: work with SharePoint tasks and e-mails in workflows
  • How to: Pass data into an InfoPath task edit form
  • How to: retrieve what the user entered into an InfoPath task edit form
  • How to: debug a workflow
  • How to: use serialization to retrieve data from an InfoPath form
  • Advanced workflow tips
  • Workflow pitfalls
  • Appendix 1 - correlation tokens

You can download all this from http://sharepointchris.googlepages.com/COB_WorkflowResources.zip

P.S. I'll be expanding on this content - the next couple of articles will be workflow-focused.

Friday, 4 January 2008

Speaking at UK SharePoint user group on workflow

For anybody interested in workflow, I'll be speaking at the UK user group next week at Microsoft's London Victoria office on 10th Jan. There are two great sessions so I'd encourage UK SharePointers who can get to London to come down. Needless to say, I'll be posting my slides/sample code etc. for those further afield.

For those considering coming down, the meeting should be fun and it looks like a high turnout - we have over 100 people registered already. My fellow speaker is SharePoint MVP and all-round good guy Andrew Woodward.

The timetable and session abstracts are:

1800 - 1830 Arrive

1830 - 1930  Workflow: A deep dive into developing workflows for SharePoint using Visual Studio and Infopath - Chris O'Brien

Of the three types of workflow available to the SharePoint developer, only Visual Studio workflows provide complete flexibility and power. Without the right information however, the learning curve can be steep for the uninitiated. In this session we’ll start with initial decisions such as SharePoint Designer workflows vs. Visual Studio workflows, sequential vs. state-machine, and InfoPath vs ASP.Net for forms. From there we’ll cover the key steps to get a workflow up and running including retrieving data from an InfoPath form, configuring workflow activities, and deployment of the workflow. Over several demos, we’ll build a basic state-machine workflow, and show why workflow is a valuable addition to any SharePoint developer’s skillset.

1930 - 2000 Snacks and drinks

2000 - 2100 Search Server 2008 – What is it, how does it affect me and should I be testing it? - Andrew Woodward

A look at the recently announced Search Server 2008.  We will walk through an installation and demonstration of the new Federated Search capabilities, what’s provided out of the box, what you can get from free, how to leverage sites using Live.com and how to federate results from Google. We will discuss how this fits in with your existing WSS and MOSS deployments and the release timescales.


Registration/more info is at http://suguk.org/forums/thread/7300.aspx - please add to the thread leaving your full name

Monday, 17 December 2007

Using the SharePoint Content Deployment Wizard

So if you've read the earlier posts about the tool (Introducing the SharePoint Content Deployment Wizard and When to use the SharePoint Content Deployment Wizard) and figure this is a useful tool, let's go onto the next level of detail. Generally speaking the Content Deployment Wizard 'just works', but if you want to know more about the different options, read on. This post contains reference information and a guide to some deployment scenarios at the end.

Firstly, let's remind ourselves of some of the fundamental things to remember when moving content using the Content Migration API (the underlying SharePoint API used by the tool):

  • dependencies of selected content (e.g. referenced CSS files, master pages) can be evaluated - in the tool they are automatically included in the export - check 'Exclude dependencies of selected objects' to disable this
  • all required content types, columns etc. are automatically included in the export
  • in contrast to STSADM export, it is possible to retain GUIDs during deployment (where objects are not being reparented) - check 'Retain object IDs and locations' to enable this
  • no filesystem files (assemblies, SharePoint Solutions/Features etc.) are deployed - these must already be present on the target for the import to succeed)
  • the following content does not get captured by the Content Migration API - alerts, audit trail, change log history, recycle-bin items, workflow tasks/state

In particular it's the 2nd and 3rd points which make the API (and the Wizard) a good way to deploy content in SharePoint.

What can be deployed?

The Content Deployment Wizard allows any content to be selected for export - site collections, webs, lists/document libraries, folders, right down to individual list items and files. Objects in the treeview can be added to the export by right-clicking them, which for a web, brings up a menu shown below:



These are explained:-

  • 'include all descendents' - exports the container and anything beneath it
  • 'exclude descendents' - exports the container only
  • on webs only, the 'include content descendents' option is shown - this will include all immediate content such as lists/libraries, but will exclude all child webs of the web.

Note that on the import, the Wizard will bring in all the contents of the selected .cmp file(s) - there is no option to partially import a package. Hence if different import options are required for different content, the exports should be broken into separate chunks.

Export options

On the export settings screen, numerous options can be applied to exports:

  • 'Exclude dependencies of selected objects' - by default the Content Migration API will automatically include dependent objects of whatever you select. This can include CSS files, master pages, images and the like, but also list items which are displayed on a page included in the export. This can be turned off with this checkbox so only the objects you select are exported.
  • 'Export method' (options are 'ExportAll', 'ExportChanges') - for now ExportAll is the option to select, ability to export changes only will come in a future release
  • 'Include versions' (options are 'LastMajor', 'CurrentVersion', 'LastMajorAndMinor', 'All') - should be self-explanatory
  • 'Include security' (options are 'None', 'WssOnly', 'All')' - note that since security is defined at the level of a web, selecting one of the include security options for a smaller object (e.g. list) actually exports security for the entire web. Both 'WssOnly' and 'All' export SharePoint item-level object permissions, so if you're using SharePoint groups to manage security for example, both the actual permissions and groups will be carried over, and you can add a different set of users/AD groups on the destination. See Migrating Security Information on MSDN for more details.

Import options

On the import we also have several options, some of which correspond to options selected on the export:

  • 'Import web URL' (actually shown on the 'Bind to site' screen) - this is used for reparenting operations only. If you are just moving content from source to destination but are not changing the location in the structure, this textbox can remain blank. Alternatively, for operations where a web or list is being imported but the parent web will not be the exact same web on the destination, the URL of the new target web URL should be entered.

    Note that the later option to 'Retain object IDs and locations' should not be selected when reparenting, since we are changing the location in this case.
  • 'From single file'/'From multiple files' options - the Wizard always exports with file compression enabled, so when exporting content over 25MB, files are split into several files at this threshold. When importing from such an export, select the 'From multiple files' option and browse to the folder. In the textbox, enter the 'base filename' - this should be the name of the first file without the number e.g. 'MyExport.cmp' rather than 'MyExport1.cmp'. 
  • 'Retain object IDs and locations' - this setting requires particular consideration. Duplicate GUIDs are not permitted in one database (i.e. SP web application), so the choice often depends on what you are importing. If you are taking a site from development to production, the object GUIDs will not yet exist on the destination, so I check the box to ensure the objects are assigned the same IDs in both environments, and all linkages are preserved. If you are reparenting a list or web, you will leave the box unchecked, so that new GUIDs are assigned are the location can therefore be changed.

    I highly recommend reading the content listed in the 'Useful links' section at the end of the article to properly understand this setting.
  • 'Include security' - this allows security information in a package to be imported, assuming one of the options to include security was selected on the export
  • 'Version updates' - allows control over whether new versions should be added to existing files, or whether the existing version should be replaced etc.
  • 'User info update' - allows control over whether 'last modified' information should be imported. Often this only makes sense if the same set of users exist in the source and destination

Scenarios quick reference

The following table lists the most common settings for a given deployment task:

Deployment item

Typical settings

Entire site collection
  • Site collection should first be created on the destination.
  • When exporting, select 'include all descendents'.
  • When importing for the first time, ensure 'retain object IDs and locations' is checked.
  • Select one of the 'include security' options if you wish to deploy object permissions and users
Web
  • When exporting, select 'include all descendents'.
  • When importing for the first time, ensure 'retain object IDs and locations' is checked if web will have same parent as on source.
  • If web will have a different parent, do not check retain object IDs and locations' and ensure 'import web URL' is specified
Document library/list
  • When exporting, select 'include all descendents'.
  • When importing for the first time, ensure 'retain object IDs and locations' is checked if list will have same parent as on source (i.e. not reparenting).
  • On subsequent imports, ensure 'import web URL' is specified if not importing to the root web, and do not check 'retain object IDs and locations'
File/list item
  • Ensure the parent library/list exists on the destination
  • Do not check 'retain object IDs and locations' if the item already exists on the destination

 

So hopefully that's some useful reference information. On a final note, the next beta version with much improved treeview performance should be ready over the next week or so!

Useful links

Friday, 14 December 2007

When to use the SharePoint Content Deployment Wizard

Following my introduction to the tool last time, today I want to try to help position the tool for people who aren't sure if it could be useful to them or for what scenarios - if you only take one thing away from my postings on the Content Deployment Wizard it should be this.

I see the 'value-add' of the Content Deployment Wizard over existing deployment methods such as STSADM export/content deployment in Central Administration to be:

  • ability to "cherry-pick" content to deploy using a treeview - this is from entire site collection down to individual list item or file. (This is the big one since the standard SharePoint tools do not supply a method to do this)
  • ability to control whether object GUIDs are retained - this is required for scenarios where the destination should be a mirror-image of the source, such as staging/production environments for the same site
  • ability to move certain objects (limited to webs and lists in the initial release) to a new location on the import target, known as 'reparenting'

I would suggest the tool could well have a place in your SharePoint toolbox, but it's likely to be something you use every now and then, rather than all the time. The two main scenarios where I use the tool are:

  • at the end of the development phase when I need to move a site from development to staging/production. Here, the tool allows me to be sure that all relationships/linkages between objects will be preserved (so no problems with ListViewWebParts/DataViewWebParts/InfoPath forms for example)
  • any odd occasions where I have a need to move a particular document library/list, or a particular set of files (e.g. master page, page layouts, CSS etc.). This assumes by the way, that the files were not deployed with a feature - I wouldn't recommend mixing the deployment methods like that.

N.B. It should also be possible to use the tool for ongoing updates to specific files/list items through different environments in a development/test/staging/production situation. An example of this is deployment of just master pages, page layouts and CSS files on a WCM site (meaning all other content authored by the client [e.g. in the 'Pages' library] does not get overwritten on the target) but I haven't had the opportunity to try this on a real project yet.

Some areas where the tool cannot be used (i.e. the tool does not yet support this usage) are:

  • exporting only changes since a certain timestamp (change token) from a site
  • importing individual list items/files to a new location on the target (reparenting)

On the last point, the 'new location' would be a document library or list, since these items have to be in such a container - they cannot exist at the root of a web. Currently the tool supports reparenting webs and lists, but not individual list items/files. What is currently possible with individual list items/files, is moving selected items from a source to a target where the structure is the same (e.g. move Doc2 and Doc5 from "Team documents" on the source to "Team Documents" on the target). Usefully, whenever a file is exported/imported using the tool, the associated list item is also deployed, meaning metadata updates to column values are deployable.

Hopefully that might help you understand where the Wizard fits in. If you're thinking the Wizard could be useful to you from time to time, stay tuned for my next post which will have more detailed 'usage' information.