Monday, February 3, 2014

SharePoint 2013: Workflow Manager Installation and configuration issues…

Installing and configuring Workflow Manager 1.0 can be frustrating, if you do not follow the right steps. I installed Workflow Manager 1.0 and ran the default configuration successfully. After installation, I figured that I need to install following cumulative update in the order specified below.

SNAGHTML206d45

After installation of above CUs, I was able to create a WF Manager based site workflow and publish it successfully and I thought I cracked it!

But when I tried to browse the site workflow, the page threw an error. The ULS log suggested that there was an error in Data Access Layer. So I fired up the SQL Server Profiler and figured out some database entries were missing in following table. See the screen below for the table and the error text:

image 

Microsoft.Workflow.TestServiceHost.exe Error: 0 : System.IO.InvalidDataException: A required Workflow Configuration 'WorkflowServiceScopeSnapshotProcessBatchSize' is not present. Please add this configuration value.

I put the entry my self, don’t ask how I came up with values, I put random large numbers because I couldn’t wait to see my workflow working. I put about 4 entries in that table before and finally, the error changed to something else. To be precise, now the error was a missing parameter in a Stored Procedure and finally, I gave up thinking that there must be something terribly wrong here… I decided to uninstall it


Uninstalling Workflow Manager in Single Server environment:


http://social.msdn.microsoft.com/Forums/windowsazure/en-US/86162367-5d43-4f8e-81d4-12440d9dbcde/error-when-running-workflow-form-list-item-workflow-manager-sharepoint-2013?forum=wflmgr


Steve mentioned the steps to uninstall Workflow Manager from the dev environment and I was able follow the steps and uninstall it. For the sake of completeness, I provide a screenshot of un-installation steps below (thank to Steve)


image


followed by successful installation…


After uninstalling, I tried following video tutorial to install and configure Workflow Manager Successfully!


Please consider every single step in this tutorial as important, if you want to see the workflow manager up and running in your dev environment.


http://msdn.microsoft.com/en-US/library/dn201724

Thursday, January 16, 2014

SharePoint 2013: Toggling between minimized and debugged version of java scripts

There is no doubt that including a minimized versions of your scripts (and css files) is best practice, not only for SharePoint but for any Web Application. However, this practice is often ignored by many programmers. Minimized version of script significantly reduces the page’ payload, especially for large javascript (and css files). Just to give you a comparison, debug version of JQuery 2.0.3 version is 246KB but its minimized counterpart is only 82KB.

On the other hand, debugged version of scripts are convenient for developers because setting breakpoints and debugging using developer toolbars is easier.

Now, what if we could archive best of both the worlds! What if we could toggle between debugged version and minimized version without re-deploying anything? Well, I would love it and so I thought about using the technique that I described here.

Note following though:

  • Even though this post uses some SharePoint 2013, with just a little tweak it can be applied to SharePoint 2010 as well.
  • The technique here shows only one JavaScript file, but as many JavaScripts as you want can be included.
  • Similar technique can be used for CSS files as well
  • I have used a couple of site collection level FEATURES which are visible on Site Collection Features page. But if you prefer, you may make them hidden and activate them using PowerShell.

Let’s go to the business now!

The first step is to create a UserControl in your solution and put it under ControlTemplates mapped folder as shown below:

image

Define a property in your code behind of the User Control as shown below:

   1:   private bool _minimized = false;
   2:   
   3:          public bool Minimized
   4:          {
   5:              get { return _minimized; }
   6:              set { _minimized = value; }
   7:          }



Now refer your scripts on in the .ascx file as shown below:

<% if (Minimized == true) {%>
<script type="text/javascript" src="/_layouts/MyApp/scripts/jquery-2.0.3.min.js"></script>
<!-- Other files goes here-->
<% }else{ %>
<script type="text/javascript" src="/_layouts/MyApp/scripts/jquery-2.0.3.js"></script>
<!-- Other files goes here-->
<%} %>




You may have to provide your own script paths..



Next, create two FEATURES and Add two empty elements using VS.NET’s “Empty Element” project item as shown below:


image


image


Add one empty element to each feature. You may use your own naming conventions for your FEATUREs and Elements.


The content of MimimizedScript –> Elements.xml file would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="AdditionalPageHead" ControlSrc="/_controltemplates/15/MyApp/MyAppScriptReferences.ascx" Sequence="20">
<Property Name="Minimized">true</Property>
</Control>
</Elements>



And the content of RegularScripts—>Elements.xml file would look like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="AdditionalPageHead" ControlSrc="/_controltemplates/15/MyApp/MyAppScriptReferences.ascx" Sequence="21">
<Property Name="Minimized">false</Property>
</Control>
</Elements>



Note the path to my user control, in SharePoint 2010, the path would look different.


Deploy your solution and get ready to see it in action. I have two FEATURES and I have activated the minimized version as shown below:


image 


When minimized version FEATURE is activated, I enjoy the performance benefits of minimized version of my scripts.


image


If I want to debug my JavaScript, all I need to do is deactivate the old feature and activate the feature which provisions regular debug version of scripts. As soon as I do that, regular JavaScript becomes available as shown below:


image


I think this is very convenient but it may not be necessarily the best option. If you find this useful or have a better idea, please leave comments below.


Tuesday, January 14, 2014

SharePoint Productivity Tip: Windows Search Service

As a SharePoint Developer, I often need to look into the SharePoint hive to look for Features, List Templates, Layouts etc. For instance, if you know the Feature ID and want to locate the Feature in SharePoint hive (14-hive for SP10, 15-hive for SP13), you are out of luck if you search for the Feature ID using windows explorer. Below is the screenshot:

SNAGHTML1a9e135

So what do we do? It seems you have to depend on window’s search infrastructure which is not installed by default on most SharePoint development server.

At high level, you need to install the Windows Search Service and Add 14/15 hive to the indexed location and let the search service finish the indexing.

Here is what I did on Windows Server 2012:

  • Go to Server Manager and Select “Add Roles and Features” from Manage option in top right corner.
  • Select the Windows Search Service option and finish the installation

image

  • You may have to restart the server at this time
  • Locate the “Indexing Options” in the windows settings
  • Add 14/15 hive to the indexing options as shown below

image

  • Let windows finish the indexing. On my PC it took less than 5 minutes, the exact time may vary depending on your machine configuration!

Now, its time to get excited! Look for the original search term that you were looking for and see it yourself!

image

Here is another example: I wanted to see how does SharePoint use AdditionalPageHead delegate control and so I looked for that word now in 15-hive (another tip is to add 14/15 hives into Libraries as highlighted below in green) and wow here is exactly what I was looking for!

image

Trust me! Serious SharePoint developers need this frequently and I am sure it will increase your productivity! Good Luck…

And yes, feel free to leave comments and tell me how it helped you…

Spinning SharePoint Timer Jobs from Site Collection Configuration

My recent content management engagement required to have several long running jobs or tasks that must run in background. For instance:

1. Analyzing Checked out files and sending reminders to the individuals if the number of days (since the file was checked out) exceed certain threshold limits

2. Removing links from other content when a particular content is removed or archived from the system

3. User wants to see all the alerts that he subscribed in all webs in the site collection

4. Sending a reminders to authors to review the content when a review time was specified in the content and that date is approaching

wow, the list goes on…

Well, we could have created one timer job for each scenario and scheduled them at different time. While that is perfectly fine, had some additional design goals for which this approach fall short. Our primary goals were:

1. Ability to monitor the background job and check its status right from site collection, without logging on to server on which it was run and go through ULS logs

2. Ability to run the job on demand without requiring farm level permissions

3. Ability to look into all individual tasks that the job performed and receive an email if anything unusual happened during the execution.

4. Ability to spin up a one time timer job based on an action by an end user and the timer job sends the required information or processes the request later.

So, I came up with a custom architecture which satisfies all of the above goals and achieve even more. While, I won’t be able to share the code because of confidentiality, I can share the details of the architecture. Please feel free to leave comments or contact me if you like the approach and need additional detail.

Here are the main components of this timer framework:

1. When a feature is enabled at Site Collection level, the framework creates two lists: Task Definition List, Task Trace List.

In task The Task definition list contains at least following information:

  • Title of the Task as it appears in the timer job definitions on central admin
  • GUID to track this definition
  • Schedule – to store an XML based schedule definition for the timer job
  • Schedule Type: matches with SharePoint Schedule Types such as one time, hourly, weekly etc.
  • Trace Level – one of the few trace level that my framework supports. A concept similar to OTB diagnostic logging
  • Provisioning Status – a status which shows whether a corresponding SharePoint timer job is created or not
  • Assembly – Full Name of the assembly which contains the execution code of this timer job
  • Class – Full name of the class which is derived from our custom interface ITimerTask interface
  • TaskData – A random XML data that your timer job needs. Framework will automatically make this data available when above Class is called when the timer job runs
  • NotifyTo – Can contains email address of person(s) who is interested in knowing the status of the timer job. The framework takes care of sending the email notification when developer sets the status of the job to anything other than “Success”

The Task Trace list contains at least following columns:

  • GUID – Identifies the corresponding timer job definition
  • ExecutionDateTime – Date and time when the timer job was executed
  • Status – Custom Status such as Success, Warning, Failed, SuccessWithWarning, FailedWithWarning etc.
  • Trace – Contains detailed trace information registered by developer while implementing the execute method of custom ITimerTask interface. Framework takes care of putting the trace information in this list. All that developer needs to to is call a method. For example, .RegisterTrace (“required list was not found”, TraceLevel.Warning)

2. A Web Application level feature is installed by the framework which creates two timer job definitions at Web Application level: Timer Task Scheduler Job and Timer Task Cleanup job.

Let’s see what exactly they do!

TimerSchedulerJob runs at a regular interval (say 5 minutes) and looks for any un-provisioned job in the The Task definition list that I mentioned above. Once it finds, it create a SharePoint Timer Job and schedules it as specified in the definition list. This newly created timer job is a generic timer job, all it does is loads the assembly and the class specified in the definition, prepares the required data for the job and calls the execute method. That means developer’s implementation of Execute method of my custom interface gets called.

Developer of this framework is responsible for:

  • Getting the required data passed as Execute method parameter, along with lot of other information like an instance of SPSite object in which the definition was created
  • Write core logic
  • Set the final status of the job
  • Register all required trace messages by calling method parameter’s RegisterMessage method

After calling the execute method, framework looks at the registered trace messages and the status of the task set by developer. The framework sends email notification specified in the definition. The framework also creates an entry in the Timer Task Trace list at the site collection level that I specified above.

Aha! so finally all dots are connected!

Finally, the Timer Task Trace clean up job removes the trace older than pre configured number of days.

So here is the experience of creating a new timer job for a developer, once the framework is enabled using Web App and Site Collection level feature:

  1. Create a class derived from ITimerTask class of my framework which has just one method called Execute
    • The core responsibility of a developer in order to use the framework effectively are specified above..
  2. Deploy the assembly to GAC
  3. Create a definition in the Timer Task Definition list at site collection level. Alternatively, create the onetime definition dynamically on click of a user action
  4. Monitor the log and see what happened when the framework executed the timer job defined in the definition.

This is one of the most useful framework that I have built and the developers in the team love it because it opens up a lot of possibilities. My developers don’t need to learn how to develop a timer job but they are able to create one and execute them with so much ease. Without a doubt this is my favorite too!

Tuesday, July 17, 2012

SharePoint: Showing Last Published Version to Authors

Quick 5 minute post! Hope it helps!

Recently, our content authors wanted to see the most recent published version without going to Version History. Unfortunately, they hate to use SharePoint’s Version History and let me mention – they hate the ribbon too Sad smile.

Anyways, they were okay with a button put in the side bar of the page which can only be displayed to Authors. And they wanted to view the page in a new window so that they can compare with draft version (thank god, they were okay with comparing the content manually Smile)

Rather than accessing the versions on server side, we decided to access the version information only when a button is clicked. Once again the wonderful Client Side Object model saved a lot of work for us. The script on click of button would look this (and that’s it for today)



this.ctx = SP.ClientContext.get_current();
this.page = ctx.get_web().getFileByServerRelativeUrl(window.location.pathname);
ctx.load(page);

ctx.executeQueryAsync(Function.createDelegate(
this, this.ShowVersion), Function.createDelegate(this, this.ShowError));
function ShowVersion() {
var versionID = page.get_uiVersion();
if (versionID < 512)
SP.UI.Notify.addNotification(
'This page was never published.', false);
else if (versionID % 512 == 0)
SP.UI.Notify.addNotification(
'You are currently viewing Published Version.', false)
else{
window.open(window.location
+ '?PageVersion=' + 512*(versionID % 512), '_blank')
}

}
function ShowError() {
SP.UI.Notify.addNotification(
'Unable to perform the action. Please report the issue.', false)
}

Saturday, January 21, 2012

Where is my Rating Feature?

Many of you know new new Rating FEAURE of SharePoint 2010. There are many resources out there to learn about using Rating Feature but here, I would talk about what it takes to make the Rating Feature available to you.

I will not waste time, ALL of following must be TRUE for rating feature to be available for your lists:

1. You must be using SharePoint Server product. According to Compare SharePoint Editions, Rating is NOT available to SharePoint Foundation that comes free with Windows Server 2008.

2. You must have configured User Profile Service in Central Administration.

3. The User Profile Service must be associated with your Web Application

Note: Step 2 and 3 are described here (and many other places, I am sure)

http://dxjo.net/dxit/2011/09/activate-and-enable-rating-in-sharepoint-2010/

4. This is perhaps not documented at many places, but you must NOT have created your site from a Blank site template. If yes, you are out of luck. But wait… there is one last chance, if you started with blank site template and you are okay with enabling Publishing Features at site collection level, you can still make it available! 

DISCLAIMER: I do not recommend and I have not tested the side effects of enabling publishing feature on sites created from blank site template. Personally, I would try to avoid it!

Alternatively, you can just try enabling the hidden rating FEATURE by using following PowerShell command. When I tested following, I was able to enable rating on announcement rating and save my rating, the average rating was also calculated depending upon the scheduled jobs. I did not test further:

Enable-SPFeature 915c240e-a6cc-49b8-8b2c-0bff8b553ed3 –URL http://yoururl.com

Also note that SharePoint staples the Rating FEATURE to following Site Templates:




<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="STS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="STS#2" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#1" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#2" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#3" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="MPS#4" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="WIKI#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLOG#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SGS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BDR#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="OFFILE#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="OFFILE#1" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PWA#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PWS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPS#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSMSITE#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOPIC#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNEWS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNHOME#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSSITES#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSBWEB#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSCOMMU#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSREPORTCENTER#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSPORTAL#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SRCHCEN#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="PROFILES#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="CMSPUBLISHING#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSPORTAL#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSREPORTCENTER#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSTOPIC#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNEWS#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSNHOME#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SPSSITES#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="SRCHCEN#0" />

<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="CMSPUBLISHING#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#0" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#1" />
<FeatureSiteTemplateAssociation Id="915c240e-a6cc-49b8-8b2c-0bff8b553ed3" TemplateName="BLANKINTERNET#2" />


The Rating FEATURE is hidden and Publishing Feature depends on it and that’s the reason it becomes available to you when you activate Publishing Feature.

Thursday, January 12, 2012

Sending Emails to SharePoint Group

Recently, I came across a situation where I had to include SharePoint group in To field of “Send Email” activity in a reusable workflow designed in SharePoint Designer. I just included it thinking that it would work (just like hundreds of other developers I guess) but I was wrong. Well, that was my last activity in the workflow. After it was deployed, it would NOT send emails to members of the SharePoint Group and worse than that, it would not show any error, the workflow appears completed but it does NOT terminate itself but the page gets published. As a result, content authors cannot start a new workflow because previous workflow was not terminated even though item was published. SharePoint not sending emails is understood but I would be happier it it logged something in the workflow history or terminated the workflow. It took me many change/deploy cycles before I could figure it out.

I posted a question on here: Definitive answer to sending emails to SP Group from Workflow and on MSDN forum too, but did not get an answer for couple of days and then thought about figuring out solution that works for me (yes, there could be other solutions out there).

Solution and Assumptions: It was safe to assume in my situation that SharePoint users will directly be added to the group to which I need to send emails.

So the simplest solution was to get develop a custom activity which can be consumed by SharePoint designer. The activity outputs list of emails given the SharePoint group or individual’s name. The designer can take the output and store it in Workflow variable. And finally, the variable is used in “To” field of the Send Email Activity.

So following solution needs to be revisited (or tested) if the SharePoint group includes other groups or active directory groups.

Code

Here is my activity looks like:

using System;
using System.ComponentModel;
using System.Linq;
using System.Workflow.ComponentModel;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
namespace MyProduct.Workflows.Activities
{
public class ExpandSharePointGroup : Activity
{
public static DependencyProperty __ContextProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__Context",
typeof(WorkflowContext), typeof(ExpandSharePointGroup));

[Description(
"Context")]
[Browsable(
true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(__ContextProperty)));
}
set
{
base.SetValue(__ContextProperty, value);
}
}


public static DependencyProperty GroupNameProperty = DependencyProperty.Register("GroupName",
typeof(string), typeof(ExpandSharePointGroup));

[Description(
"SharePoint Group Name")]
[Category(
"MyProduct Workflow Activities")]
[Browsable(
true)]
[DesignerSerializationVisibility
(DesignerSerializationVisibility.Visible)]
public string GroupName
{
get
{
return ((string)
(
base.GetValue(GroupNameProperty)));
}
set
{
base.SetValue(GroupNameProperty, value);
}
}

public static DependencyProperty FieldValueProperty = DependencyProperty.Register("FieldValue",
typeof(string), typeof(ExpandSharePointGroup));

[Description(
"Field Value")]
[Category(
"MyProduct Workflow Activities")]
[Browsable(
true)]
[DesignerSerializationVisibility
(DesignerSerializationVisibility.Visible)]
public string FieldValue
{
get
{
return ((string)
(
base.GetValue(FieldValueProperty)));
}
set
{
base.SetValue(FieldValueProperty, value);
}
}



protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{

SPWeb web
= (SPWeb)__Context.Web;

try
{
if (string.IsNullOrEmpty(GroupName))
{

return ActivityExecutionStatus.Closed;
}

string strOutput = string.Empty;
strOutput
= Helper.ResolveToEmailName(__Context, GroupName);
FieldValue
= strOutput;
}
catch (Exception ex)
{
// TODO: Log error here
return ActivityExecutionStatus.Faulting;
}


return ActivityExecutionStatus.Closed;
}

}

}

In order to make sure that above activity is available to SharePoint designer, we need to deploy a .Actions file to {SharePointRoot}\Template\1033\Workflow which is shown below:


<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions>
<Action Name="Expand SharePoint Group Members"
ClassName
="FULL CLASS NAME"
Assembly
="FULL ASSEMBLY NAME"
AppliesTo
="all"
Category
="My Custom Workflow Activities">
<RuleDesigner Sentence="Expand Email for %1 it in: %2">
<FieldBind Field="GroupName" Text="SharePoint Group" DesignerType="TextBox" Id="1"/>
<FieldBind Field="FieldValue" Text="Field Value" DesignerType="ParameterNames" Id="2"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="In" DesignerType="Hide" />
<Parameter Name="GroupName" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="FieldValue" Type="System.String, mscorlib" Direction="Out" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>

Finally, you need to add a web.config entry under authorizedTypes section:



<authorizedType Assembly="Full Assembly Name" Namespace="Your Name space" TypeName="Class Name or *" Authorized="True" />


Note: After your assembly is deployed in GAC, Actions file is deployed on SharePoint root and web.config entry is added (as shown above), you need to close the SharePoint Designer and open it again before you can see your activity in list of available actions.


Hope this helps!