Intelligent Security, Compliance and Privacy in Office 365 session at SharePoint Saturday Lisbon

Last Saturday, I delivered a session at the SharePoint Saturday event, that was for the first time held in Lisbon.

My session was entitled “Intelligent Security, Compliance and Privacy in Office 365”,  focused on security, compliance and privacy around the Office 365 platform. The main topics of the session were:

  • Office 365 Platform Security
  • Privacy (Differentiated Access Policies, External Sharing, Granular Access Controls)
  • Compliance (Data Loss Prevention, Information Rights Management, Mobile Device Management)
  • Transparency (Customer Lockbox, SharePoint Insights)
  • Advanced Threat Protection

You can find the slide deck here.

Related Links:

SharePoint Saturday Lisbon 2016 web site
My SlideShare web site
|create|it| web site

SharePoint Tunning Session

Last week I delivered a session at SharePoint Portuguese Community 36th event.

My session was entitled SharePoint Tunning, focused on techniques used to improve the performance of a SharePoint farm. The main topics of the session were:

  • SQL Server performance tips (most of the presentation, since performance in SharePoint is guaranteed mainly at SQL Server level)
  • SharePoint farm performance tips
  • SharePoint development tips

You can find the slide deck here. I hope you understand Portuguese since the slide deck is in my mother language :).

Related Links:

 

Error “Value does not fall within the expected range” using SPFile.MoveTo in SharePoint

Problem:

Recently, I caught the following error in a piece of code that was trying to move a document between two SharePoint document libraries inside the same SharePoint site (SPWeb):

“Value does not fall within the expected range”

Cause:

The error was being throwned because an absolute URL was being specified as the destination URL in the SPFile.MoveTo method.

Solution:

According to the MSDN article at http://msdn.microsoft.com/en-us/library/ms468280.aspx, we have:

public void MoveTo(
	string newUrl
)

The newUrl parameter in the SPFile.MoveTo method specifies the destination URL. As explained in the article, the SPFile.Moveto can only be used to move files inside the same SharePoint site (SPWeb) and so relative URLs should be used. Example:

SharePoint and “The form cannot be displayed in the browser because the use of session cookies” error

Problem:

Recently, a customer has created a new SharePoint environment and added an alternate access mapping (AAM) with the URL containing a “_” character (ex: http://intranet_QA). When opening an InfoPath form in an IE browser (from IE8 up to IE11), the following error was being throwned:

"The form cannot be displayed in the browser because the use of session cookies has been disabled in the current browser settings. In order to load the form, session cookies must be allowed."

In other browsers, the InfoPath form opened successfully, without throwing any error.

Cause:

This behavior is due to the fact that Internet Explorer doesn’t accept cookies with host names that contain the “_” character.

Solution:

To solve this problem, the “_” character was removed from the URL. In this example:

Hope this helps!

SharePoint 2007 Workflow – Using the OnWorkflowItemDeleted activity

Introduction

One of the most exciting features included in SharePoint 2007 is workflow support and the possibility of developing our own custom workflows (for more information about workflow development in the SharePoint 2007 platform please click here).

While it is possible to develop workflows to automate a series of activities without any human intervention, the full power of workflows in the SharePoint platform can only be achieved with human-oriented workflows. These workflows are characterized by creating and assigning tasks to users that must complete them in order to fulfill the purpose of the workflow. Some examples of out of the box human-oriented workflows that are included with MOSS 2007 are Approval, Collect Feedback and Issue Tracking.

In the SharePoint world, the most common usage of human-oriented workflows is in the context of document libraries, to perform some work when a new document is uploaded into a document library. Typically, in such scenario, tasks are created and assigned to users throughout the workflow lifetime that will complete them until the workflow purpose has been fulfilled. Having the workflow goal to be achieved is the desired and most common scenario, but unexpected actions may occur before the end of the workflow such as a user to delete the document. The following example will show you how to handle this situation in a custom workflow by using the OnWorkflowItemDeleted activity.

Example

The OnWorkflowItemDeleted activity can be used within a custom workflow to handle an item deletion in any SharePoint list. In this example, this activity will be used to delete all uncompleted tasks associated with the deleted item. After dragging and dropping the OnWorkflowItemDeleted activity into the workflow designer, the Invoke property must be set to the event handler that will perform the work.

For the purpose of this example, please consider the simple approval workflow in the following image:

In this workflow, a parallel activity with two branches is used. One contains the main workflow logic while the other contains a OnWorkflowItemDeleted activity that will handle the item deletion. The Invoke event property was set to be handled by an event handler method called OnWorkflowItemDeleted_Invoked. Let’s take a look at the code:

public sealed partial class ApprovalWorkflow : SequentialWorkflowActivity
{
    /// <summary>
    /// Handles the workflow item deleted event.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnWorkflowItemDeleted_Invoked(object sender, ExternalDataEventArgs e)
    {
        //delete uncompleted tasks when 
        //an item is deleted
        SPWorkflow workflowInstance = 
            workflowProperties.Workflow;
        SPWorkflowTaskCollection taskCollection = 
            GetWorkflowTasks(workflowInstance);
        for (int i = taskCollection.Count; i > 0; i--)
        {
            SPWorkflowTask task = 
                taskCollection[i - 1];
            using (SPWeb web = 
                workflowProperties.Web)
            {
                if (task[SPBuiltInFieldId.TaskStatus]
                    .ToString() != SPResource.GetString
                    (new CultureInfo((int)web.Language, false),
                    "WorkflowTaskStatusComplete", new object[0]))
                {
                    task.Delete();
                }
            }
        }
    }

    /// <summary>
    /// Reads the workflow tasks. This method 
    /// is implemented because the Tasks property
    /// of the SPWorkflow instance takes a 
    /// while to be populated.
    /// </summary>
    public static SPWorkflowTaskCollection 
        GetWorkflowTasks(SPWorkflow workflowInstance)
    {
        SPWorkflowTaskCollection taskCollection = null;
        bool tasksPopulated = false;
        while (!tasksPopulated)
        {
            try
            {
                taskCollection = workflowInstance.Tasks;
                tasksPopulated = true;
            }
            catch { }
        }

        return taskCollection;
    }
}

In the previous example, the following actions are being performed:

  • In the OnWorkflowItemDeleted_Invoked event handler, the workflow instance task collection is obtained by calling the GetWorkflowTasks method;
  • The task collection is iterated and the each task status is checked to verify if it is still uncompleted;
  • If so, the task is deleted.

SharePoint 2007 – Updating List Content Types Event Handlers on Site Content Type Update

Introduction

One of the areas where the SharePoint 2007 platform (both WSS 3.0 and MOSS 2007) still needs to improve is deployment despite all the major improvements made when compared to its antecessor, the 2003 version. One of the faults I ran recently into is related with updating list content types event handlers when site content types event handlers are updated.

For those who don’t know, let’s start by a brief introduction about content types and explain the difference between a site content type and a list content type. Content types are a new concept introduced in SharePoint 2007 and is basically a reusable collection of settings that can be applied to a certain category of content. Content types basically define the metadata (they are composed by a reusable set of columns called site columns) and behaviors (event handlers, workflows, etc) of a certain category of content. They are stored centrally and are reusable throughout a site or site collection depending on its scope. For more information about content types please click here.

So what is the difference between a site content type and a list content type? Probably, the easiest way to explain this is to think of site content types as templates (in the sense that they are reusable) and list content types as instances of those templates. Site content types are defined at the site level and are visible to the site where they are created and all its subsites (this concept is called content type scope) and they are reusable and not bound to any specific list, meaning that they can be associated with any list within the sites under its scope. When a site content type is associated with a list, a copy of the site content type is copied into the list, creating a new instance. This instance is called a list content type.

Example

The best approach to deploy content types is to use features (for more information about deploying site content types as a feature please click here). Registering content type event handlers can also be achieved using a feature (check this article for more information) or by using Patrick Tisseghem’s Event Handler Explorer. The problem is when there are already list content types based on a site content type and we need to add a new event handler to it and have all list content types also updated. In my experience, both using the feature model approach or using Patrick’s application didn’t update the list content types. The remainder of this post will show you how to do this programmatically.

The following example basically uses a console application to register a event handler and associate it to a content type.

class Program
{
    private const string URL = "http://myIntranet";
    private const string CONTENT_TYPE = "MyContentType";
    private const string EVENT_RECEIVER_NAME = "MyEventReceiver";
    private const string EVENT_RECEIVER_ASSEMBLY =
        "MyEventReceivers.MyEventReceiver, MyEventReceiver, " +
        "Version=1.0.0.0, Culture=neutral, PublicKeyToken=5989549632a535cf";
    private const string EVENT_RECEIVER_CLASS_NAME = 
        "MyEventReceivers.MyEventReceiver";

    static void Main(string[] args)
    {
        //open site collection
        using (SPSite siteCollection = new SPSite(URL))
        {
            using (SPWeb site = siteCollection.OpenWeb())
            {
                //get content type
                SPContentType contentType = 
                    site.ContentTypes[CONTENT_TYPE];
                // add ItemUpdating event handler
                ContentTypeHelper.AddEventHandler(contentType, 
                    EVENT_RECEIVER_NAME, EVENT_RECEIVER_ASSEMBLY, 
                    EVENT_RECEIVER_CLASS_NAME, 10000, 
                    SPEventReceiverInstallerType.ItemUpdating);

                site.Update();
            }
        }
    }
}
internal class ContentTypeHelper
{
    /// <summary>
    /// Register an event handler in a list
    /// </summary>
    /// <param name="contentType">SharePoint Content Type</param>
    /// <param name="receiverName">Event receiver name</param>
    /// <param name="className">Event handler class name</param>
    /// <param name="sequenceNumber">Sequence number</param>
    public static void AddEventHandler(SPContentType contentType, 
        string receiverName, string assemblyName, 
        string className, Int32 sequenceNumber, 
        SPEventReceiverInstallerType eventReceiverInstallerType)
    {
        //get assembly full name
        string assemblyFullName = System.Reflection.Assembly
            .GetAssembly(Type.GetType(assemblyName)).FullName;

        // remove existing event handlers
        RemoveEventHandlers(contentType, assemblyFullName);

        SPEventReceiverDefinition receiverDefinition = null;

        //ItemAdding event handler
        if (SPEventReceiverInstallerTypeHelper.CheckOption
            (eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemAdding))
        {
            // add ItemAdding event handler
            receiverDefinition = contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemAdding;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //ItemAdded event handler
        if (SPEventReceiverInstallerTypeHelper.CheckOption
            (eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemAdded))
        {
            // add ItemAdded event handler
            receiverDefinition = contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemAdded;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //ItemUpdating event handler
        if (SPEventReceiverInstallerTypeHelper.CheckOption
            (eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemUpdating))
        {
            // add ItemUpdating event handler
            receiverDefinition = contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemUpdating;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //ItemUpdated event handler
        if (SPEventReceiverInstallerTypeHelper.CheckOption
            (eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemUpdated))
        {
            // add ItemUpdating event handler
            receiverDefinition =contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemUpdated;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //ItemCheckingIn event handler
        if (SPEventReceiverInstallerTypeHelper.
            CheckOption(eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemCheckingIn))
        {
            // add ItemCheckingIn event handler
            receiverDefinition = contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemCheckingIn;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //ItemCheckedIn event handler
        if (SPEventReceiverInstallerTypeHelper.CheckOption
            (eventReceiverInstallerType, 
            SPEventReceiverInstallerType.ItemCheckedIn))
        {
            // add ItemCheckedIn event handler
            receiverDefinition = contentType.EventReceivers.Add();
            receiverDefinition.Name = receiverName;
            receiverDefinition.Type = 
                SPEventReceiverType.ItemCheckedIn;
            receiverDefinition.Assembly = assemblyFullName;
            receiverDefinition.Class = className;
            receiverDefinition.SequenceNumber = sequenceNumber;
            receiverDefinition.Update();
        }

        //update content type - setting the updateChildren 
        //parameter to true will update list content types
        contentType.Update(true);
    }

    /// <summary>
    /// Remove an event handler registration from a list.
    /// </summary>
    /// <param name="list">SharePoint list</param>
    /// <param name="receiverClass">Event receiver class name</param>
    internal static void RemoveEventHandlers(
        SPContentType contentType, 
        string receiverClass)
    {
        SPEventReceiverDefinition receiverDefinition = null;

        // cycle through all the receiver definitions and 
        //delete the receiver with the supplied class name
        for (int i = contentType.EventReceivers.Count - 1;
            i >= 0; i--)
        {
            receiverDefinition = contentType.EventReceivers[i];
            if (receiverDefinition.Class == receiverClass)
            {
                receiverDefinition.Delete();
            }
        }
    }
}

The statement that does the trick is "contentType.Update(true);" in the end of the AddEventHandler method. Setting the updateChildren parameter of this method to true will update list content types.

SharePoint 2007 – Checking if a Workflow Instance is Completed

Introduction

This blog post will show you how to check if a certain workflow instance is completed in a SharePoint list. In this example, let’s assume that the default "Documents" document library in a team site is configured with a Collect Feedback workflow and that a new instance is started every time a new document is added to the library. The workflow is configured with 2 reviewers. My purpose is to start a custom workflow (associated with a custom workflow) on each document every time all reviewers have reviewed them, i.e., every time a Collect Feedback workflow instance is completed.

Example

The following example shows how the previously described scenario can be implemented. One first note is that when a workflow instance gets completed on a list item (thus changing the workflow status of the item), the item is not updated. This means that the obvious solution that would be to have an event handler running on the document library to detect the completion of the workflow for each document can’t be implemented. The following example uses an alternative approach that makes use of an event handler associated with the "Task" content type, that is used on workflow tasks lists. The event handler will run every time a task is updated and will get the task associated list item (the document) and start a custom workflow on the document when its Collect Feedback workflow instance gets completed. Let’s take a look at the code:

/// <summary>
/// Event handler associated with the "Task" content types.
/// </summary>
public class WorkflowCompletionCheckerEventReceiver : SPItemEventReceiver
{
    //custom content type name
    private const string CUSTOM_CONTENT_TYPE_NAME = 
        "My Custom Content Type";
    //task list workflow list id field
    public const string TASK_LIST_WORKFLOW_LIST_ID_FIELD_NAME = 
        "ows_WorkflowListId";
    //task list workflow item id field
    public const string TASK_LIST_WORKFLOW_ITEM_ID_FIELD_NAME = 
        "ows_WorkflowItemId";
    //collect feedback workflow id
    public const string COLLECT_FEEDBACK_WORKFLOW_ID = 
        "46c389a4-6e18-476c-aa17-289b0c79fb8f";
    //custom workflow id
    public const string CUSTOM_WORKFLOW_ID = 
        "96be29b0-1be4-4b99-a703-656e5470962b";

    /// <summary>
    /// Event handler for item updated event.
    /// </summary>
    /// <param name="properties"></param>
    public override void ItemUpdated(SPItemEventProperties properties)
    {
        //start a custom workflow on a list item when a 
        //collect feedback workflow gets completed
        StartCustomWorkflowOnTaskItemCompletion(properties);
    }

    /// <summary>
    /// Checks if the "Collect Feedback" workflow 
    /// instance on an item is completed. This method checkes a 
    /// workflow task and verifies if they have been created by a 
    /// "Collect Feedback" workflow and if the associated item is of 
    /// my custom content type. If yes, it verifies if the workflow has 
    /// been completed and if complete, starts a custom workflow 
    /// on the associated item.
    /// </summary>
    /// <param name="properties"></param>
    private void StartCustomWorkflowOnTaskItemCompletion
        (SPItemEventProperties properties)
    {
        
        //get the task item
        SPListItem listItem = properties.ListItem;
        if (listItem != null)
        {
            //get associated item id and associated list id

            //list id - ows_WorkflowListId field
            object associatedWorkflowListId = 
                listItem[TASK_LIST_WORKFLOW_LIST_ID_FIELD_NAME];
            //item id - ows_WorkflowItemId field
            object associatedWorkflowItemId = 
                listItem[TASK_LIST_WORKFLOW_ITEM_ID_FIELD_NAME];

            //get associated item
            if (associatedWorkflowItemId != null 
                && associatedWorkflowListId != null)
            {
                //get task associated list item using 
                //its list id and item id
                SPListItem associatedListItem = listItem.Web.Lists
                 .GetList(new Guid(associatedWorkflowListId.ToString()), false)
                 .GetItemById(int.Parse(associatedWorkflowItemId.ToString()));

                //check if item is of my custom content type
                if (associatedListItem.ContentType.Name
                    .Equals(CUSTOM_CONTENT_TYPE_NAME))
                {
                    //check if item has any workflow association
                    if (associatedListItem.Workflows.Count > 0)
                    {
                        //iterate workflow associations and lookup 
                        //the collect feedback association
                        foreach (SPWorkflow workflow in 
                            associatedListItem.Workflows)
                        {
                            Guid collectFeedbackWorkflowId = 
                                new Guid(COLLECT_FEEDBACK_WORKFLOW_ID);
                            //check if collect feedback workflow 
                            //instance is completed
                            if (workflow.ParentAssociation.BaseId 
                                == collectFeedbackWorkflowId
                                && workflow.InternalState 
                                == SPWorkflowState.Completed)
                            {
                                //if collect feedback workflow instance is 
                                //completed, start custom workflow that is 
                                //associated with the item
                                StartContentTypeWorkflow(associatedListItem, 
                                    CUSTOM_WORKFLOW_ID);
                            }
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Starts a workflow associated to a content type programmatically.
    /// </summary>
    /// <param name="properties"></param>
    public static void StartContentTypeWorkflow(SPListItem listItem,
        string workflowId)
    {
        //start specified workflow that is associated with the item
        SPWorkflowManager manager = listItem.Web.Site.WorkflowManager;
        //get list item content type
        SPContentType contentType = listItem.ContentType;
        //get list item workflow associations
        SPWorkflowAssociationCollection associationCollection = 
            contentType.WorkflowAssociations;
        //iterate all item workflow associations
        foreach (SPWorkflowAssociation association in associationCollection)
        {
            Guid workflowGuid = new Guid(workflowId);
            //check if current workflow association matches 
            //the specified workflow
            if (association.BaseId == workflowGuid)
            {
                string data = association.AssociationData;

                //start workflow
                SPWorkflow wf = manager.StartWorkflow(listItem, association, 
                    data, false);
            }
        }
    }
}

In the previous example, the following actions are being performed:

  • The ItemUpdated event in the event handler is being handled;
  • In the event handler, a reference to the task is obtained;
  • The associated list item is obtained using two columns of the task list item:
    • "ows_WorkflowListId" – holds the id of the list where the associated list item is created;
    • "ows_WorkflowItemId" – holds the id of the associated list item;
  • The content type of the associated list item is checked for a match to my custom content type;
  • If there is a match, the list item workflow associations are checked for a match with the Collect Feedback workflow;
  • If there is a match and the workflow instance is completed, the custom workflow is started.

InfoPath 2007 – Conditional SUM

This blog post will show you how to create an expression box in an InfoPath 2007 form whose value is based on the result of an conditional sum. Consider the following InfoPath form template:

The previous image shows a simple expense report. To support the introduction of the expense information, a Repeating Table control is used with three columns:

  • Expense – Expense Description. A simple Text Box control;
  • Value – Expense value. A simple Text Box control;
  • Expense Type – Drop-down List Box control that allows 4 expense types: Food, Land Travel, Air Travel and Parking.

Below the repeating table there are 5 expression boxes that show the total amount of the expense report and the total amount for each expense type. The total amount expression box is based on a simple sum expression and each of the expense type expression boxes are based on conditional sums filtered by the value of the expense type Drop-down List Box control. The expressions used for each expense type are the following:

Expression Box Expression XPath Expression
Expense Total sum(expensevalue) sum(my:accounting/my:expensevalue)
Food Expense Total sum(accounting[expensetype = "Food"]/expensevalue) sum(my:accounting[my:expensetype = "Food"]/my:expensevalue)
Land Travel Expense Total sum(accounting[expensetype = "Land Travel"]/expensevalue) sum(my:accounting[my:expensetype = "Land Travel"]/my:expensevalue)
Air Travel Expense Total sum(accounting[expensetype = "Air Travel"]/expensevalue) sum(my:accounting[my:expensetype = "Air Travel"]/my:expensevalue)
Parking Expense Total sum(accounting[expensetype = "Parking"]/expensevalue) sum(my:accounting[my:expensetype = "Parking"]/my:expensevalue)

As the previous table shows, the conditional sum expressions for each expense type are relatively simple and pretty straightforward for those who are familiarized with XPath, since XPath syntax is used for each expression. This comes as no surprise since the underlying data of the InfoPath form is stored in XML. The following image shows an expense report example filled with some sample values:

SharePoint 2007 – New instances of this workflow template are currently disallowed

When creating a new custom workflow project using Visual Studio 2008, a strong key file (.snk) file is automatically included in the project to sign the workflow assembly. A few days ago, I started developing a SharePoint 2007 Sequential Workflow project. After finishing developing the workflow, I deployed it and started testing it by associating the workflow to a content type (Site Settings > Site Content Type Gallery > Site Content Type > Workflow settings). I then noticed that the strong key file that was being used to sign the workflow assembly wasn’t the one I usually use and modified the project settings so that it started using it. After recompiling the project and redeploying the workflow, I started to get an error message: "New instances of this workflow template are currently disallowed". The reason for this message to appear is that a new version version of the workflow had been deployed (same assembly with a different public key token due to the change in the strong key file used to sign the assembly). In these situations, the existing workflow associations made from older versions of the workflow are automatically set to "No New Instances", meaning that new instances of the workflow for those workflow associations are not allowed. The image below shows the Remove Workflow page with my custom workflow association set to "No New Instances". To access this page, go to Site Settings > Site Content Type Gallery > Site Content Type > Workflow settings > Remove Workflows if the workflow is associated with a content type.

 

To solve the problem, all you have to do is to set the workflow association to "Allow" and new instances of the workflow can again be created.

SharePoint 2007 – Start a Workflow Programmatically

Introduction

This blog post will show you how to start a workflow programmatically every time an item is updated in a SharePoint list. For those who don’t know, Windows Workflow Foundation (WF) is the new engine for building custom workflows (you can find more information about WF on the official web site). It was released as part of the .NET Framework 3.0 along with Windows Presentation Foundation (WPF) and Windows Communication Foundation (WCF). In the SharePoint world, workflows can be associated both to a list and to a content type. Both options are valid and the choice for one or the other option depends on your needs:

  • If you want to run a workflow in a list, independently of the content type of each list item, you should associate the workflow to a list. If you have more than one content type associated to the list, care should be taken in the workflow implementation since each content type can have different columns that may not be present in the other content types;
  • If you want to run the workflow on all items of a specific content type, you should associate the workflow to a content type. The workflow will be associated to all list items from that content type in all lists across a site or the whole site collection (depending on the scope of the workflow, typically defined in its deployment feature) that are associated with the content type.
Example

The following example shows how both options can be addressed. This example uses an event handler and starts a workflow every time an item is updated in a SharePoint list.

 

public class ProgrammaticWorkflowInitiationEventReceiver : SPItemEventReceiver
{
    /// <summary>
    /// Event handler for item updated event.
    /// </summary>
    /// <param name="properties"></param>
    public override void ItemUpdated(SPItemEventProperties properties)
    {
        //use this method if the workflow is associated to a list
        StartListWorkflow(properties);

        //use this method if the workflow is associated to a content type
        StartContentTypeWorkflow(properties);
    }

    /// <summary>
    /// Starts a workflow associated to a list programmatically.
    /// </summary>
    /// <param name="properties"></param>
    private void StartListWorkflow(SPItemEventProperties properties)
    {
        //get list item from event handler properties
        SPListItem listItem = properties.ListItem;

        using (SPWeb web = listItem.Web)
        {
            using (SPSite site = web.Site)
            {
                //obtain an instance of SPWorkflowManager 
                //which will be later used to start the workflow
                SPWorkflowManager manager = site.WorkflowManager;
                //get item's parent list
                SPList parentList = listItem.ParentList;
                //get all workflows that are associated with the list
                SPWorkflowAssociationCollection associationCollection = 
                    parentList.WorkflowAssociations;
                //lookup and start the worflow
                LookupAndStartWorkflow(listItem, manager,
                    associationCollection, "FDEDCC37-4D8A-44ba-8E22-57B2669A887C");
            }
        }
    }

    /// <summary>
    /// Starts a workflow associated to a content type programmatically.
    /// </summary>
    /// <param name="properties"></param>
    private void StartContentTypeWorkflow(SPItemEventProperties properties)
    {
        //get list item from event handler properties
        SPListItem listItem = properties.ListItem;

        using (SPWeb web = listItem.Web)
        {
            using (SPSite site = web.Site)
            {
                //obtain an instance of SPWorkflowManager 
                //which will be later used to start the workflow
                SPWorkflowManager manager = site.WorkflowManager;
                //get item's content type
                SPContentType contentType = listItem.ContentType;
                //get all workflows that are associated with the content type
                SPWorkflowAssociationCollection associationCollection = 
                    contentType.WorkflowAssociations;
                //lookup and start the worflow
                LookupAndStartWorkflow(listItem, manager,
                    associationCollection, "FDEDCC37-4D8A-44ba-8E22-57B2669A887C");
            }
        }
    }

    /// <summary>
    /// Lookup and start the workflow.
    /// </summary>
    /// <param name="listItem"></param>
    /// <param name="manager"></param>
    /// <param name="associationCollection"></param>
    /// <param name="workflowId"></param>
    private static void LookupAndStartWorkflow(SPListItem listItem, 
        SPWorkflowManager manager, 
        SPWorkflowAssociationCollection associationCollection, 
        string workflowId)
    {
        //iterate workflow associations and lookup the workflow to be started
        foreach (SPWorkflowAssociation association in associationCollection)
        {
            //if the workflow association matches the workflow we are looking for,
            //get its association data and start the workflow
            Guid workflowGuid = new Guid(workflowId);
            if (association.BaseId == workflowGuid)
            {
                //get workflow association data
                string data = association.AssociationData;

                //start workflow
                SPWorkflow wf = manager.StartWorkflow(listItem, association, data);
            }
        }
    }
}

In the previous example, the following actions are being performed:

  • The ItemUpdated event in the event handler is being handled. For demonstration purposes, I am assuming that two workflow associations were created, one to a list and another to a content type;
  • A call is made to the StartListWorkflow method, which starts the workflow associated with a list;
  • A call is made to the StartContentTypeWorkflow method, which starts the workflow associated with a content type.