Wednesday, November 11, 2009

Event Receivers: How to add event receivers to a sharepoint list

Scenario: How to add custom event receivers to a specific sharepoint list using SharePoint Object Model

Approach:

SPList list = web.Lists["MyList"];

string assemblyName = "CustomSharePointEventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d517db3ed1131947";

string className = "CustomSharePointEventReceivers.MyEventReceivers";
list.EventReceivers.Add(SPEventReceiverType.ItemAdding, assemblyName, className);
list.EventReceivers.Add(SPEventReceiverType.ItemAdded, assemblyName, className);
list.EventReceivers.Add(SPEventReceiverType.ItemUpdating, assemblyName, className);



Note: In general, custom event receivers to a specific list will be added through Sharepoint features by defining a feature event receivers by extending SPFeatureReceiver. In the SPFeatureReceiver, override all the 4 methods i.e, FeatureInstalled,FeatureActivated, FeatureDeactivating and FeatureUninstalling. In these overriden methods, write above code piece.

Event Receivers: Delete all event receivers associated to a list

scenario: How to delete all the event receivers associated to a list?

Approach:


List eventreceivers = new List();

foreach (SPEventReceiverDefinition EventReceiver in list.EventReceivers)
{
eventreceivers.Add(EventReceiver);
}

foreach (SPEventReceiverDefinition er in eventreceivers)
{
er.Delete();
}


Note: We can not have only 2nd foreach loop alone. The current list of event receivers associated to a list gets updated everytime a delete operation is performed. So, an exception will be raised if you use only delete operation.

Change Workflow Task Status from any SharePoint custom component

Scenario: I have a custom workflow running. I want to change status a task from another sharepoint custom component like webpart or so.

Approach:
If we update the workflow task directly by changing the status field value to Completed or to any other status, then usually workflow raises an exception "This task is currently locked by a running workflow and cannot be edited." To get rid of this error, you can use following routine:


private void CompleteTask(string taskTitle)
{
try
{
Hashtable taskHash = new Hashtable();
taskHash[SPBuiltInFieldId.TaskStatus] = "Completed";

string currentuser = SPContext.Current.Web.CurrentUser.Name.ToLower();
Guid siteID = SPContext.Current.Site.ID;
Guid webID = SPContext.Current.Web.ID;

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(siteID))
{
using (SPWeb web = site.OpenWeb(webID))
{
web.AllowUnsafeUpdates = true;
SPList wfTasksList = web.Lists["WorkflowTasks"];
SPQuery queryWFTasks = new SPQuery();
queryWFTasks.Query = <<>>;
queryWFTasks.ViewAttributes = "Scope=\"RecursiveAll\"";

SPListItemCollection wfTaskItems = wfTasksList.GetItems(queryWFTasks);

if (wfTaskItems.Count != 0)
{
foreach (SPListItem item in wfTaskItems)
{
if (item.Title.ToLower().Equals(taskTitle.ToLower()))
{
SPWorkflowTask.AlterTask(item, taskHash, true);
break;
}
}
}

web.AllowUnsafeUpdates = false;
}
}
});
}
catch (Exception ex)
{
// Log the exception here
}
}

SharePoint Workflow : Get From Email Address Configured in Central Admin

Scenario: I want to send email from workflow SendEmail activity. How to get From email address configured in central administration for my sharepoint application.

Note: For every sharepoint application, we can configure out-going email address information in central admin. This can be done in following steps:
a. Go to central admin site -> Application Management
b. Under "SharePoint Web Application Management" section, check for the link "Web application outgoing e-mail settings" and click on it.
c. Select the Web Application for which you want to set the out going email informatino.
d. Fill the following fields:
  • Outbound SMTP server:

  • From address:

  • Reply-to address:

  • Character set:

Approach:
In the SendEmail activity execute code, you write following instruction to get email set in above "From address:" field:

workflowProperties.Site.WebApplication.OutboundMailSenderAddress

How to get SharePoint user information from Person or Group field in SharePoint List

Scenario: I have a SharePoint list with various fields. One of them is Person field, in which I am storing the value selected through People Picker. How to get the Person value from this field through SharePoint Object Model.

Approach:


// Step1: Get SharePoint Item object
SPListItem Item = SPContext.Current.Web.Lists["MyList"].Item[0];

// Step2: Get user field object by passing field display name
SPFieldUser userField = (SPFieldUser)Item.Fields.GetField("Created By");

// Step3: Get user field value object
// SPBuiltInFieldId.Author refers to Created By field's internal name
SPFieldUserValue fieldValue = (SPFieldUserValue)userField.GetFieldValue(Item[SPBuiltInFieldId.Author].ToString());

// Step4: Get user object
SPUser userVendor = fieldValue.User;

Never use Thread.Sleep in SharePoint Workflows

From last 2 months, i was very busy in developing custom workflows. So, I didn't get chance to blog my experience during all these days. I really got great experience by working on a custom workflow. I am trying to put my experience in various posts including this post.

Coming to the main point, never ever use Thread.Sleep in SharePoint workflows

Reason: If we deploy the custom workflow with Thread.Sleep instructions, then IISReset will keep these instructions out of memory and the workflow will struck there.
Alternative: Use appropriate inbuilt or custom workflow activities.

Sunday, September 13, 2009

SharePoint Custom Workflow Error (related to Serialization): WinWF Internal Error, terminating workflow

I created a custom workflow using VS 2008 for MOSS 2007.
In my SP site, I created a custom list CONTACTS with following columns:
Region - Single Line Text
Dept1 - Person Group
Dept2 - Person Group
Dept3 - Person Group

In this workflow, I need to retrieve users from CONTACTS list and create tasks for the users included within the groups associated to Dept columns.
I created custom workflow and ran it by associating to another list.
I got following error:
"WinWF Internal Error, terminating workflow Id# d4a5785f-22bf-42e8-bfca-d561bdb44ed7 System.Workflow.Runtime.Hosting.PersistenceException: Type 'Microsoft.SharePoint.SPUserCollection' in Assembly 'Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' is not marked as serializable. ---> System.Runtime.Serialization.SerializationException: Type 'Microsoft.SharePoint.SPUserCollection' in Assembly 'Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' is not marked as serializable. at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() at System.Runtime.Seri... ...alization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) at System.Workflow.ComponentModel.Activity.Save(Stream stream, IFormatter formatter) at System.Workflow.Runtime.Hosting.WorkflowPers... Workflow Infrastructure 98d4 Unexpected ...istenceService.GetDefaultSerializedForm(Activity activity) at Microsoft.SharePoint.Workflow.SPWinOePersistenceService.SaveWorkflowInstanceState(Activity instanceState, Boolean unlock) at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation) --- End of inner exception stack trace --- at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation) at System.Workflow.Runtime.WorkflowExecutor.ProtectedPersist(Boolean unlock)"

What I understood from above exception in the SharePoint Logs:
The exception raised by WF is because of SPUserCollection object is not marked as serializable.
I made following changes to my code:
a. Marked my custom workflow class as serializable as all the objects within WF must be serializable. I did this by placing the attribute on top of the class as below:
[Serializable()]
public partial class SampleWorkflow : SequentialWorkflowActivity
{
}
b. Removed declaration of SPUserCollection objects as these obj are not serializable. To store user information for each department, i created separate hashtable and stored the data while retrieving the user information, instead of storing SPUser or SPUserCollection objects.

Reference Blog Posts, which helped me in solving this problem:
http://social.msdn.microsoft.com/Forums/en-US/sharepointworkflow/thread/01cde268-5148-4ff7-847e-1d03803c91c2
http://social.msdn.microsoft.com/Forums/en-US/sharepointworkflow/thread/9bd91cba-558c-456a-8f3c-e0e829145ec6/
http://blogs.msdn.com/sharepoint/archive/2006/11/28/developing-workflows-in-vs-part-5-code-your-workflow.aspx