Saturday, June 18, 2011

SharePoint 2010 Workflow: Dealing with events in custom activities

Introduction

In the past few months I've been responsible for designing a workflow solution to support the automation of an asset management acquisitions process for a customer in the US. The last time I worked on a large workflow project, which touched thousands of users and had a truly global reach, was back in December 2009. I was a little bit rusty and have spent one or two days in the last month getting back up to speed. In this blog post, I'd like to talk about a common scenario which will prove as much of a reference for me in the future as anything else.

The common scenario I'd like to deal with is one where there is a requirement to assign a task to users who will only be known at runtime. Further, when these tasks are assigned, depending upon the actual task/form which is in use, you may want to perform different actions.


The Requirements

  • Provide task management for 1 or more users who will only be known at runtime


  • Provide a customized summary report to an individual, who will only be known at runtime, which outlines what tasks were assigned, to whom, and what their response was.

If you are familiar with the MOSS 2007 ECM Starter Kit workflow examples then you have probably seen the widely referenced WssTaskActivity custom activity within the ECM Activities solution. This solution was fine for MOSS 2007 but I actually found it a little heavy in terms of code for a lot of scenarios. A much nicer, cleaner and relevent example is the one which has been put together by Scott Hillier. I would point you to his article at

http://www.shillier.com/archive/2010/08/04/CreatingMultipleandParallelTasks%20inSharePoint2010Workflow.aspx


and recommend you read that and become comfortable with the concepts before reading this article further.


The Problem

The main advantage of custom activities in workflow is to increase reusability and repeatability. Using the SPTaskActivity custom activity from the article posted above is a great starting point, but there's something missing. Quite often, what we need to do is provide some notification in the workflow which hosts the custom activity that something has happened e.g. a task has changed. It is possible to deal with all events relating to the custom activity within the custom activity itself but this doesn't provide reusability. We need to achieve this within the host.


The Solution

The solution to this problem lies in the use of DependencyProperty objects to raise a custom event within the host workflow.


  • Create a custom EventArgs class

  • Create a DependencyProperty which points to a generic EventHandler

  • Raise the event at the appropriate point e.g. onTaskChanged_Invoked from within your Custom Activity

  • Handle the event within your host workflow

Create a custom EventArgs class

First we need to decide what information we need to pass back to the host. For the purposes of this blog post, I've used the TaskEventArgs class from the MOSS 2007 ECM starter kit. The class is very simple and is given below. Create a new class file within your workflow project and paste this code.

[Serializable()]
public class TaskEventArgs : EventArgs
{
#region Private Variables

private SPWorkflowTaskProperties beforeProperties = null;
private SPWorkflowTaskProperties afterProperties = null;
private bool result = false;
private string executor = null;
private ExternalDataEventArgs externalArgs = null;

#endregion

#region Public Properties

public SPWorkflowTaskProperties BeforeProperties
{
get
{
return this.beforeProperties;
}
set
{
this.beforeProperties = value;
}
}

public SPWorkflowTaskProperties AfterProperties
{
get
{
return this.afterProperties;
}
set
{
this.afterProperties = value;
}
}

public bool Result
{
get
{
return this.result;
}
set
{
this.result = value;
}
}

public string Executor
{
get
{
return this.executor;
}
set
{
this.executor = value;
}
}

public ExternalDataEventArgs ExternalArgs
{
get
{
return this.externalArgs;
}
set
{
this.externalArgs = value;
}
}

#endregion

#region Constructors

public TaskEventArgs(SPWorkflowTaskProperties before, SPWorkflowTaskProperties after, string executor, ExternalDataEventArgs externalArgs)
{
this.beforeProperties = before;
this.afterProperties = after;
this.executor = executor;
this.externalArgs = externalArgs;
}

#endregion

}



Create a DependencyProperty which points to a generic EventHandler

This is pretty simple, and we can use code snippets to create this for us. You will want to ensure you specify that it is a generic EventHandler based on the TaskEventArgs class you've previously added to your workflow project.

public static DependencyProperty InvokeEvent = DependencyProperty.Register("Invoke", typeof(EventHandler<TaskEventArgs>), typeof(SPTaskActivity));

[Description("Invoke")]
[Category("Invoke Category")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public event EventHandler<TaskEventArgs> Invoke
{
add
{
base.AddHandler(SPTaskActivity.InvokeEvent, value);
}
remove
{
base.RemoveHandler(SPTaskActivity.InvokeEvent, value);
}
}


The result should look something like the below






Raise the event at the appropriate point e.g. onTaskChanged_Invoked from within your Custom Activity

The next thing we need to do is actually raise this event. In this instance, I wanted the Custom Activity to really do nothing when onSPTaskChanged_Invoked was fired, I needed this to be handled externally by the host workflow. We can achieve this by first raising the event in the following way:

private void onSPTaskChanged_Invoked(object sender, ExternalDataEventArgs e)
{
TaskEventArgs args = new TaskEventArgs(SPBeforeProperties,
SPAfterProperties, null, e);

base.RaiseGenericEvent<TaskEventArgs>(InvokeEvent, this, args);
}

Handle the event within your host workflow

We then need to handle the event in the workflow. Inspect the properties of your custom activity and notice you now have a new event which you can handle.

private void InvokedFromSPTaskActivity(object sender, TaskEventArgs e)
{
bool result = Boolean.Parse(e.AfterProperties.ExtendedProperties["Result"].ToString());
}

The result should look something like the below:





Summary

We have seen how we can leverage a DependencyProperty based generic EventHandler to add more reusability and flexibility to our custom activities within workflow. This is an important technique and one that, at first, may not seem straight-forward but is surprisingly simple.

Thursday, May 5, 2011

Deploying InfoPath 2010 forms for custom workflow with SharePoint 2010 and Visual Studio 2010

I've spent a little time recently structuring a solution in Visual Studio and TFS for an upcoming project that relies heavily on workflow. There are a couple of nuances that I thought I'd document given that the information out there at present seems fragmented at best.

The aim of this blog post is to provide guidance on how to structure a Visual Studio 2010 SharePoint workflow project that includes the deployment of InfoPath forms. This article will not repeat advice which is already in abundance that relates to core workflow development concepts. I assume you're familiar with writing custom workflow in Visual Studio 2008 and MOSS 2007.

Above we see the default solution structure for a state machine workflow.

Add a new Module named "InfopathForms"

You can remove the "Sample.txt" placeholder file. When you do this, notice that Elements.xml for the module will update automatically.


Add your InfoPath form to your InfopathForms module


Double check to ensure that the "Deployment Type" property of your InfoPath form is set to Element File and that the path is "InfopathForms\".

Your elements .xml file should now look like the following

<?xml version="1.0" encoding="utf-8"?>

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<Module Name="InfopathForms">

<File Path="InfopathForms\InitiationForm.xsn" Url="InfopathForms/InitiationForm.xsn" />

</Module>

</Elements>


By default, your feature.xml will look like the following:

<?xml version="1.0" encoding="utf-8" ?>

<Feature xmlns="http://schemas.microsoft.com/sharepoint/">

<Properties>

<Property Key="GloballyAvailable" Value="true" />

</Properties>

</Feature>


Now we need to modify our feature.xml to register the location of the forms we want to use and set the correct feature receiver. If you were used to using VSeWSS when developing workflows for MOSS 2007, these steps will have been undertaken for you automatically, you may have had to do little more than modify the path for the RegisterForms property below.

<?xml version="1.0" encoding="utf-8" ?>

<Feature xmlns="http://schemas.microsoft.com/sharepoint/" ReceiverAssembly="Microsoft.Office.Workflow.Feature, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"

ReceiverClass="Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver">

<Properties>

<Property Key="GloballyAvailable"Value="true" />

<Property Key="RegisterForms" Value="InfopathForms\*.xsn"/>

</Properties>

</Feature>



There are two additions here. The first is:

<Property Key="RegisterForms" Value="InfopathForms\*.xsn"/>

This tells our feature the location of the forms we want to deploy. In this case it is all of the .xsn files within the InfopathForms module.

The second modification is the feature receiver assembly and class:

ReceiverAssembly="Microsoft.Office.Workflow.Feature, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"


ReceiverClass="Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver"

This is necessary in order for the forms to be present within the "Manage Form Templates" screen within Central Administration and is a necessary precursor to making your forms "Workflow Enabled".

At this point there is a ton of other work you need to do around building your workflow, registering your form URNs within the workflow elements file and any deserialization activities you need to do in order to take data from your initiation or association form into your workflow. None of this information is repeated here and I would point you towards MSDN and the numerous blog articles out there on these subjects.

Once you've deployed your solution, your form should now be registered in the correct location. Below we can see our IniitationForm at the top of the list within Central Administration.


Using Modules in this way to manage form templates means we simply need to drag and drop new xsn files into our Module and they will be automatically deployed with our solution. We still of course need to modify the workflow elements file to register the URNs etc but it's one less thing to think about and certainly makes deployment easier.

I hope you've found this article useful and that it saves you some time when transitioning from workflow development using VSeWSS in MOSS 2007 to Visual Studio 2010 with SharePoint 2010. Thanks for reading.

Wednesday, May 4, 2011

Migrating from Lotus Notes 8.5 to Microsoft SharePoint 2010: Real-world tales from the trenches

I've just published a new blog post on Nothing But SharePoint which outlines a real-world migration from Lotus Notes 8.5 to SharePoint 2010. I actually went live with this project on the day of the SharePoint 2010 global launch.

Read more here...


Sunday, November 14, 2010

European SharePoint Best Practices Conference 2011


I am honoured to be contributing to next year's European SharePoint Best Practices Conference 2011 as a speaker.

The conference takes places on April 11th - 13th 2011 at the Queen Elizabeth II Conference Centre, London.

The conferences features 96 sessions , 69 speakers (includes the 15 community track speakers and sessions) and this excludes the sponsor partner sessions, It will be the SharePoint conference to attend in Europe in 2011 no other conference outside Microsoft’s in the US will have this many talented SharePoint speakers in one conference. Exhibitor Party / Conference Party / SharePint night and full set of Video Recorded,MP3 and PowerPoint DVDs sent out after the conference, We are expecting the conference to sell out so booking early is recommended.

More information at http://www.sharepointbestpractices.co.uk/index.html



Monday, August 30, 2010

Self-Service BI and PowerPivot Session @ SharePoint UK User Group - 2nd September 2010

I'll be delivering another session at the Leeds/Bradford SharePoint UK User Group on 2nd September 2010 on Self-Service BI and PowerPivot. I'll update this post to provide a link to the slide deck after the session. In the meantime, see the agenda below:

Agenda:

- What is PowerPivot?

- Architecture – PowerPivot for Excel and PowerPivot for SharePoint (Analysis Services & Vertipaq mode)


- Installing PowerPivot (Dev/Prod build considerations)


- The client tools – Excel-Add In for PowerPivot


- Connecting to back end line of business applications with a worked example


- Modelling data in PowerPivot with a worked example


- PivotTables/Charts


- Slicing and Dicing data in PowerPivot


- Custom queries using DAX


- Publishing PowerPivot applications to SharePoint

Saturday, June 26, 2010

A note on SharePoint and Windows Update KB938444

A Windows Update security patch (KB938444) has been known to cause a number of issues within a SharePoint farm after its application. Errors such as:

- HTTP 404
- Server Error
- Cannot connect to the configuration database

can appear after applying the patch. You should read Microsoft’s statement on the SharePoint blog and a complete description of the problem from the Small Business Server team at the following locations:

http://blogs.msdn.com/b/sharepoint/archive/2010/06/22/installing-kb938444.aspx

http://blogs.technet.com/b/sbs/archive/2010/06/18/companyweb-and-sharepoint-central-admin-not-accessible-after-installing-kb983444.aspx

Monday, June 21, 2010

Custom Ranking Models with SharePoint 2010: Background, Value and Administrative Overview

Introduction

At a recent SharePoint user group session, where I spoke on Enterprise Search in SharePoint 2010, I was introduced as discussing the "black art" of search. The reason, I think, that search was described in that way reflects the way in which most people think about search: something that is pervasive and quickly yields the information required, usually on the first page of results. It's fair to say that the average user doesn't comprehend the complexity and effort required to maintain relevancy, and nor should they need to.


With the accuracy, power and flexibility of the big-name internet search engines comes a certain expectation from end users in terms of search engine performance behind the corporate firewall. Striving to achieve the same level of fidelity of search results behind the firewall with most Enterprise Content Management (ECM) systems presents a major challenge and administrative headache. SharePoint 2010 alleviates some of this pain and provides the ability to override the default ranking model for enterprise search results. This enables the development of application and organisation specific enterprise search applications which provide contextual results.

This article is the first of a series I'll produce on custom ranking models with SharePoint 2010. The aim of this article is to provide a background on custom ranking models, why they're important and how SharePoint 2010 supports the implementation and management of custom ranking models through PowerShell and XML configuration files.

Background on Custom Ranking Models and SharePoint 2010

The ranking model that SharePoint 2010 uses is based upon BM25F. BM25F is actually BM25, a ranking function originally implemented at London City University, with field weighting and per-field length normalisation and provides support for weightings for managed properties/metadata amongst other things. It is at this point that I would strongly urge the reader to investigate the references at the end of this article. There is an extent to which the successful implementation of custom ranking models depends upon foundation knowledge of BM25F.

There are nine ranking models available out of the box with SharePoint 2010 and these are:

  • Main Results Default Ranking Model
  • Expertise Social Distance Ranking Model
  • High Proximity Ranking Model
  • Main People Social Distance Model
  • Expertise Model
  • Name Social Distance Ranking Model
  • Name Model
  • Main People Model
  • No Proximity Model

There is no support to modify these ranking models and you must create your own. For reference purposes, these can be viewed within the MSSRankingModels table of the SSA Admin DB as shown below:


I would urge you to 'look' at these ranking models in a development environment because that will give you an idea of the power and flexibility that you can leverage through XML files alone.

Why are custom ranking models important?

Custom Ranking Models are important because they allow search results to be tailored based upon what we know is already important within our organisation. Consider an Enterprise Search Center for a Sales division within an organisation. Sales people are sales driven and have an much greater interest in specific content types and information than others e.g. a PowerPoint slide deck relating to a SharePoint sales pitch rather than an extensive Word document along the technical aspects of SharePoint Enterprise Search. Ranking Models, simply put, allow us to define what is appropriate and relevant within a specific organisation and promote/demote search results based on the criteria that we define.

The Ranking Model Schema

Microsoft provide a ranking model schema, given below, which provides a blueprint on which to validate your custom models.


<rankingModel name="string" id="GUID" description="string" xmlns="http://schemas.microsoft.com/office/2009/rankingModel">

<queryDependentFeatures>

<queryDependentFeature pid="PID" name="string" weight="weightValue" lengthNormalization="lengthNormalizationSetting" />

</queryDependentFeatures>

<queryIndependentFeatures>

<categoryFeature pid="PID" default="defaultValue" name="string">

<category value="categoryValue" name="string" weight="weightValue" />

</categoryFeature>

<languageFeature pid="PID" name="string" default="defaultValue" weight="weightValue" />

<queryIndependentFeature pid="PID" name="string" default="defaultValue" weight="weightValue">

<transformRational k="value" />

<transformInvRational k="value" />

<transformLinear max="maxValue" />

</queryIndependentFeature>

</queryIndependentFeatures>

</rankingModel>


This basic blueprint will be explored in future articles as we discuss the actual development of custom ranking models. For now, I would draw your attention to the QueryDependantFeature and QueryIndependantFeature XML elements.

Query Dependant Features within the ranking model provide the ability to define managed properties which supports dynamic ranking. Query Independent Features are static ranking properties that apply irrespective of the query issued or the results returned. Why is this important? Well… we may want to stipulate that we always want to rank PowerPoint presentations above Word documents. We could then express this as a Query Independent Feature.

Administrative Overview

The power of custom ranking models becomes even more impressive when we realise that developer intervention is not necessary for their implementation and management. The necessary tools to implement and manage custom ranking models are PowerShell and XML.

Managing custom ranking models with PowerShell

There are four PowerShell cmdlets that provide the functionality to manage existing, and implement new, ranking models.

Get-SPEnterpriseSearchRankingModel

Returns a ranking model

New-SPEnterpriseSearchRankingModel

Adds a ranking model to a shared search application

Remove-SPEnterpriseSearchRankingModel

Deletes a ranking model

Set-SPEnterpriseSearchRankingModel.

Sets the properties of a ranking model for a shared search application


These speak for themselves and I won't discuss each in turn any more than the descriptions given above. Suffice to say, I urge you to look at those cmdlets within PowerShell and take some time to understand their parameters.

Applying custom ranking models in Enterprise Search

Once you have a custom ranking model applied, using the aforementioned PowerShell cmdlets, there are three ways in which you can test the effects that other ranking models have on search results.

  1. Add rm={GUID} parameter to your search centre query string


    This is the easiest way to test the effects that a custom ranking model will have on search results and is probably the route you will take during development and testing. To quickly see how search results change based on different ranking models, you can take one of the GUIDs from the ModelId column in the MSSRankingModels table and apply that.


  2. Export Core Results WebPart, modify RankingModel parameter and reimport


    This is useful when developer intervention is not required or available. A standard out of the box Core Results Web Part can be exported to a .webpart file. An administrator is then able to modify this file and insert the GUID of the ranking model to use before reimporting the file into the page or Web Part gallery.


  3. Inherit the Core Results Web Part and wire up your ranking model in code


    This is the most likely option when there is already a development effort underway or there are other specific customisations to the Core Results Web Part needed.


What's next?

The next article will explore the development of custom ranking models. This would benefit from an entire blog post of its own. For now, and before you embark on developing custom ranking models, I would urge you to begin to understand the fundamental concepts of BM25F and begin to identify what information is important to your organisation that you can use to influence ranking.

References

Microsoft Cambridge at TREC–14: Enterprise track
http://trec.nist.gov/pubs/trec14/papers/microsoft-cambridge.enterprise.pdf