Archive

Archive for May, 2010

>Password prompt when posting a note in SharePoint 2010

May 31, 2010 1 comment

>

When you post a note within a SharePoint 2010 site the note is recorded against your user profile. If your IE zone settings have your My Site URL in the Internet Zone then you will be prompted for a password.

To resolve this issue add the SharePoint mysites URL to your Local Intranet zone.

Categories: Uncategorized

Experiences building SharePoint Designer Custom Workflow Actions

May 30, 2010 2 comments

Introduction

For some reason I have shyed away from creating custom SharePoint Designer Workflow Actions. However, one of my clients uses a fair amount of SharePoint designer based workflows.

The requirement was to retrieve the workflow id so that they could provide a user a link to the workflow status page.

With the out of the box workflow actions this is not possible. So the only method would be to build a custom workflow action. Initially, I thought the process was going to be pretty straightforward. Over the last few years I have read plenty of articles about how to build custom actions including a few of the pitfalls. However, the process wasn’t as straight forward as I thought and I hit a few problems. This blog entry will hopefully share my experiences and
explain how I figured them out. In the end the final error was a stupid eror! Though it came about through the process of fixing a previous error I had. Hopefully my experience will help others to figure this out quicker, so here goes.

Using Visual Studio I created a new Visual Studio project using the Workflow Activity project type. The first discussion to make was whether to build from the SequentialActivity or Activity base class. I decided to use a SequentialActivity base class.

The following code was created:-

public partial class GetWorkflowInstanceInfoActivity : SequenceActivity
    {
        #region Dependency Properties

        //The SharePoint workflow properties activation object
        public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register("__ActivationProperties", typeof(SPWorkflowActivationProperties), typeof(GetWorkflowInstanceInfoActivity));

        // Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty.  This enables animation, styling, binding, etc...
        public static DependencyProperty WorkflowInstanceIdVariableProperty = DependencyProperty.Register("WorkflowInstanceIdVariable", typeof(string), typeof(GetWorkflowInstanceInfoActivity));

        // Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty.  This enables animation, styling, binding, etc...
        public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(GetWorkflowInstanceInfoActivity));

        [Description("The WorkflowInstanceId for the workflow.")]
        [ValidationOption(ValidationOption.Required)]
        public string WorkflowInstanceIdVariable
        {
            get { return (string)(base.GetValue(WorkflowInstanceIdVariableProperty)); }
            set { base.SetValue(WorkflowInstanceIdVariableProperty, value); }
        }

        [Description("The Workflow Properties")]
        [ValidationOption(ValidationOption.Required)]
        public SPWorkflowActivationProperties __ActivationProperties
        {
            get { return (SPWorkflowActivationProperties)(base.GetValue(__ActivationPropertiesProperty)); }
            set { base.SetValue(__ActivationPropertiesProperty, value); }
        }

        [Description("The Workflow Context object")]
        [ValidationOption(ValidationOption.Optional)]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public WorkflowContext __Context
        {
            get
            {
                return (WorkflowContext)(base.GetValue(__ContextProperty));
            }

            set
            {
                base.SetValue(__ContextProperty, value);
            }
        }
        #endregion

        public GetWorkflowInstanceInfoActivity()
        {
            InitializeComponent();
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            ActivityExecutionStatus status = ActivityExecutionStatus.Faulting;
            try
            {
                //build up the workflow information class.
                if (__ActivationProperties != null)
                {
                    WorkflowInstanceIdVariable = __ActivationProperties.WorkflowId.ToString();
                }

                status = ActivityExecutionStatus.Closed;
            }
            catch (Exception ex)
            {
                status = ActivityExecutionStatus.Faulting;

                throw;
            }
            return status;
        }
    }

In order for project to work two references were added to the project, these were Microsoft.SharePoint.dll and Microsoft.SharePoint.WorkflowActivities.dll.

Within the code there are two special properties, __ActivationProperties and __Context. Both these properties are always made available by the SharePoint Designer workflow actions framework. This post by Phil Allen is incredibly useful and explains a lot about how the SharePoint Designer workflow actions work: http://blogs.msdn.com/b/sharepointdesigner/archive/2007/09/30/getting-to-workflow-information.aspx

The following properties are provided by the SharePoint Designer workflow action framework:

Property Name Value it is set to .Net type
__ActivationProperties __initProps, the SPWorkflowActivationProperties that was passed in via OnWorkflowActivated Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties
__ListId __list, the name of the SPList the workflow is executing on. This is typically a string datatype which contains the text of a System.Guid. System.String
__ListItem __item, the integer value of the list item in the list that the workflow is running on. System.Int32
__Context __context, the object that contains many helper methods and objects for use as part of workflow actions Microsoft.SharePoint.WorkflowActions.WorkflowContext

Once the workflow assembly compiles then it needs to be signed as the assembly needs to be deployed to the GAC.

Next a .ACTIONS file is created. This file describes how SharePoint Designer displays the Custom Action within the SharePoint Designer Workflow builder.

This ACTION file should be deployed in the 12HIVE\Templates\1033\Layouts\Workflow folder. To help you understand the ACTIONS file take a look at the WSS.ACTIONS which describes the out of the box actions.

Our action file should look like this:-

  <?xml version=”1.0″ encoding=”utf-8″ ?> <WorkflowInfo> <Actions Sequential=”then” Parallel=”and”> <Action Name=”Get WorkflowInstanceId”
ClassName=”ITSP.Foundation.SPDActivities.GetWorkflowInstanceInfoActivity”
Assembly=”ITSP.Foundation.SPDActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a52a37e312a5558″
AppliesTo=”all”
CreatesTask =”false”
Category=”Custom”> <RuleDesigner Sentence=”Store WorkflowInstanceID in %1″ AppliesTo=”all” Name=”AssignWorkflowInstanceVariable”> <FieldBind Field=”WorkflowInstanceIdVariable” Text=”variable” Id=”1″ DesignerType=”ParameterNames” /> </RuleDesigner> <Parameters> <Parameter Name=”WorkflowInstanceIdVariable” Type=”System.String, mscorlib” Direction=”Out” /> <Parameter Name=”__Context” Type=”Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions” Direction=”In” /> <Parameter Name=”__ActivationProperties” Type=”Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint” Direction=”Out” /> </Parameters> </Action> </Actions> </WorkflowInfo>  

Once the ACTION file has been created then we need to deploy our custom activity using the SharePoint solution framework. For this I recommend using WSPBuilder (http://wspbuilder.codeplex.com).

Once the Custom Action has been built as a solution deploy the solution file then deploy using stsadm –o addsolution and deploy with stsadm –o deploysolution.

The final two steps are :-

  • Ensure SharePoint can load our assembly reference to the web.config
  • Ensure that SharePoint Designer can trust the assembly

To add our assembly to the web.config add the following line within the <assemblies> </assemblies> elements :-

<add assembly=”ITSP.Foundation.SPDActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a52a37e312a5558″ />

This should be added to the web.config file which belongs to the web application in which the workflow will run.

To ensure that SharePoint Designer is able to trust the assembly then need to add another configuration setting to the same web.config file.

This should be added to the <authorisedTypes></authorisedTypes> elements.

  <authorizedTypes> <authorizedType Assembly=”ITSP.Foundation.SPDActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a52a37e312a5558″ Namespace=”ITSP.Foundation.SPDActivities” TypeName=”*” Authorized=”True” /> </authorizedTypes>  

Take special notice to the Namespace tag, I have seen examples on the web that state this should be ITSP.Foundation.SPDActivities.* when this was used then SharePoint Designer could not add the action to the workflow.

Once the solution has been deployed and the assemblies setup within the web.config we are ready to the test the workflow.

Fire up SharePoint Designer and open up the site and then:-

  • Click New->Workflow
  • Choose the Blank Workflow
  • Give the workflow a name
  • Click Next
  • Give the workflow step a name such as Get WorkflowID
  • Click on the Actions button and click the last list item to display all workflow actions available. From the categories you should have a custom group, choose Get Workflow Instance ID
  • This should now display the action and allow you to assign a variable to store the variable into.
  • Finally create another action, this time use Build a Dynamic String. This will be used to build a string and store our concatenated string to the workflow history log. Create a variable called LoggingVariable to store the results of the Build Dynamic String Action
  • Click on the f(x) button and then type “This is the workflow id: “ and then insert the variable that was used to retrieve the workflow instance id.
  • Finally create another action using “Log to History” to log the LoggingVariable into the history log
  • Click Check Workflow, everything should be ok and then click Finish.

Finally test the workflow.

All of this took a while to get working and I liked to explains some of the issues I had and then the method that was used to fix them.

Issues

Issue 1

Once the action file was created it did not appear in SharePoint designer. The fix: restart SharePoint designer and add an entry to SharePoint’s web application web.config within the <authorizedTypes> tag.

Issue 2

When the custom action did show up in SharePoint Designer, and was selected to be added as a Workflow Action nothing would show. This was because of a problem with Namespace attribute in the <authorizedType> element.

The value was ITSP.Foundation.SPDActivities.* as soon as this was changed to ITSP.Foundation.SPDActivities then the action was entered in correctly. This didn’t require a restart of SharePoint designer.
eg.

  <authorizedTypes> <authorizedType Assembly=”ITSP.Foundation.SPDActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a52a37e312a5558″ Namespace=”ITSP.Foundation.SPDActivities” TypeName=”*” Authorized=”True” /> </authorizedTypes>  

Issue 3

This problem took the longest to solve and I have seen loads of people post similar problems. This issue seemed to only affect any output fields, particularly ones which used a Field Binding with DesignerType=”ParameterNames”. The symptoms would be that once the action was added in a workflow step and you tried to select a variable to store the action’s result in it would reset back to nothing. That is the variable would never be stored properly.

The problem was caused by the action file assembly details being incorrect. The ActivityClass was set to ITSP.Foundation.SPDActivities.GetWorkflowInfo and not ITSP.Foundation.SPDActivities.GetWorkflowInfoActivity.

<Action Name=”Get WorkflowInstanceID”      ClassName=”ITSP.Foundation.SPDActivities.GetWorkflowInstanceInfoActivity”            Assembly=”ITSP.Foundation.SPDActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a52a37e312a5558″
AppliesTo=”all”
CreatesTask =”false”
Category=”Custom”>

This issue came about because of changes made when trying to fix issue 2.

Once again Phil Allen’s article helped a lot and I ended up also updating the Actions parameter file to include the following:-
The important part in the <Parameters> elements is the Type field which should include both Class and Assembly.
<Parameters>
<Parameter Name=”WorkflowInstanceIdVariable” Type=”System.String, mscorlib” Direction=”Out” />
<Parameter Name=”__Context” Type=”Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions” Direction=”In” />
<Parameter Name=”__ActivationProperties” Type=”Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint” Direction=”Out” />
</Parameters>

Note: because I was using a Parameter which had the class SPWorklowActivationProperties I had to include a reference to Microsoft.SharePoint.dll.

So how did I resolve issue 3? Well when a workflow is created within SharePoint Designer there is a moment where SharePoint downloads details of the workflow actions which are available. In doing this it cache’s the various assemblies and creates a proxy stub which is stored on your machine.
This is the reason why you need to restart SharePoint designer after you make changes to the assembly.
This proxy stub is found in your user’s %userprofile%\Local Settings\Application Data\Microsoft\WebsiteCache\ directory. Within this directory is a directory for each website that there are assembly caches for. I opened up the assembly proxy in .NET Reflector. Once opened this allowed me to workout that there were two classes in this assembly. The class that my ACTIONS file was pointing to was the wrong one!
So there were two classes being :-

  • ITSP.Foundation.SPDActivities.GetWorkflowInstanceInfoActivity
  • ITSP.Foundation.SPDActivities.GetWorkflowInstanceInfo.

It turns out that I had forgotten to rename the class properly. I had updated the workflow activity code file but not the designer.cs file!!

Once the assembly was fixed, solution redeployed and SharePoint Designer restarted then everything worked beautifully.

You can download the projects here:

 

Please let me know if you found this post useful!

Good luck

Simon

%d bloggers like this: