We will create a sandbox VS solution that has;
1- Content Type for WFs list “Workflows list, which is the list that will carry items; each item will be corresponding to a timer job”.
2- List Definition for WFs.
3- List Instance for WFs
4- Content Type for ERs list” Event Receivers list, which is the list that will have event receivers for item updating and item updated event, each item in this list will be corresponding to a timer job”.
5- List Definition ERs.
6- List Instance for ERs.
7- Workflow based on WFs Content Type and associated to WFs list.
8- Event Receivers for ERs list
Steps
1- Create a team site called Sandbox Timer Job
2- Create Sandbox Solution Called Sandbox Timer Job and point to the Sandbox Timer Job site URL.
3- Create Content Type for WFs and name it WFsCT
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Field ID="{EE2C4296-B8F6-4E14-A3BF-D6479D619630}" Name="IsWorking" DisplayName="Is Working" Type="Boolean" Description="Determines if the WF or the ER is working " Group="_Sandbox TimerJob Columns" Required="FALSE" Sealed="FALSE" />
<Field ID="{756A3594-231E-4F8D-BDED-B7CF62F72A6A}" Name="TimerJobID" DisplayName="Timer Job ID" Type="Text" Description="Timer Job ID" Group="_Sandbox TimerJob Columns" Required="FALSE" Sealed="FALSE" />
<Field ID="{1AA858AE-56E6-4495-BC08-5048DDF58257}" Name="TimerJobName" DisplayName="Timer Job Name" Type="Text" Description="Timer Job Name" Group="_Sandbox TimerJob Columns" Required="FALSE" Sealed="FALSE" />
<Field ID="{FBC6E407-8E46-4024-BA3D-7EFBDBB42370}" Name="LastTimeRan" DisplayName="Last Time Ran" Type="Text" Description="Last Time Ran" Group="_Sandbox TimerJob Columns" Required="FALSE" Sealed="FALSE" />
<!-- Parent ContentType: Item (0x01) -->
<ContentType ID="0x01007aa0ccf9b3354ce69a95c198aaa555e6"
Name="WFsCT"
Group="_Sandbox TimerJob Content Types"
Description="This content type is used forworkflows list"
Inherits="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" DisplayName="Status" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{EE2C4296-B8F6-4E14-A3BF-D6479D619630}" Name="IsWorking" DisplayName="Is Working" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{756A3594-231E-4F8D-BDED-B7CF62F72A6A}" Name="TimerJobID" DisplayName="Timer Job ID" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{1AA858AE-56E6-4495-BC08-5048DDF58257}" Name="TimerJobName" DisplayName="Timer Job Name" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{FBC6E407-8E46-4024-BA3D-7EFBDBB42370}" Name="LastTimeRan" DisplayName="Last Time Ran" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
</FieldRefs>
</ContentType>
</Elements>
4- Add List Definition From Content Type based off WFsCT and name it WFsDef and create list instance for it.
5- Modify the View to show all columns by modifying the <ViewFields>section as the following.
<ViewFields>
<FieldRef Name="Title" DisplayName="Status"/>
<FieldRef Name="IsWorking" DisplayName="Is Working"/>
<FieldRef Name="TimerJobID" DisplayName="Timer Job ID"/>
<FieldRef Name="TimerJobName" DisplayName="Timer Job Name"/>
<FieldRef Name="Log" DisplayName="Log"/>
</ViewFields>
Top
6-
List Instance with data should look.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance Title="Workflows"
OnQuickLaunch="TRUE"
TemplateType="10001"
Url="Lists/WFs"
Description="List that the workflow will be triggered from">
<Data>
<Rows>
<Row>
<Field Name='Title'> Add Task </Field>
<Field Name='IsWorking'>False</Field>
<Field Name='TimerJobID'>1</Field>
<Field Name='TimerJobName'>Add Task</Field>
<Field Name='LastTimeRan'>-</Field>
</Row>
<Row>
<Field Name='Title'> Update First Task </Field>
<Field Name='IsWorking'>False</Field>
<Field Name='TimerJobID'>2</Field>
<Field Name='TimerJobName'>Update First Task</Field>
<Field Name='LastTimeRan'>-</Field>
</Row>
</Rows>
</Data>
</ListInstance>
</Elements>
7- Create Content type for the list that will have Event Receivers, call it ERsCT. Don’t create new columns, we will use the created ones
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Parent ContentType: Item (0x01) -->
<ContentType ID="0x01001f77ec68827f46719fec01cf60c37685"
Name="ERsCT"
Group="_Sandbox TimerJob Content Types"
Description="This content type is used event receivers list"
Inherits="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" DisplayName="Status" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{EE2C4296-B8F6-4E14-A3BF-D6479D619630}" Name="IsWorking" DisplayName="Is Working" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{756A3594-231E-4F8D-BDED-B7CF62F72A6A}" Name="TimerJobID" DisplayName="Timer Job ID" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{1AA858AE-56E6-4495-BC08-5048DDF58257}" Name="TimerJobName" DisplayName="Timer Job Name" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
<FieldRef ID="{FBC6E407-8E46-4024-BA3D-7EFBDBB42370}" Name="LastTimeRan" DisplayName="Last Time Ran" Required="FALSE" Sealed="FALSE" ShowInNewForm="TRUE" ShowInDisplayForm="TRUE" ShowInEditForm="TRUE" />
</FieldRefs>
</ContentType>
</Elements>
8- Create List Definition From Content Type based of ERsCT with list instance and call it ERsDef. Also change the view to show all columns as sown above.
9- List Instance with data should look like this
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance Title="Event Receivers"
OnQuickLaunch="TRUE"
TemplateType="10002"
Url="Lists/ERs"
Description="List that the event receivers (timerjob codes) are triggered from">
<Data>
<Rows>
<Row>
<Field Name='Title'> Add Task </Field>
<Field Name='IsWorking'>False</Field>
<Field Name='TimerJobID'>1</Field>
<Field Name='TimerJobName'>Add Task</Field>
<Field Name='LastTimeRan'>-</Field>
</Row>
<Row>
<Field Name='Title'> Update First Task </Field>
<Field Name='IsWorking'>False</Field>
<Field Name='TimerJobID'>2</Field>
<Field Name='TimerJobName'>Update First Task</Field>
<Field Name='LastTimeRan'>-</Field>
</Row>
</Rows>
</Data>
</ListInstance>
</Elements>
Top
Using SharePoint designer, create a reusable workflow based off WFsCT and write the following actions then publish
The most important part here is updating the Event Receivers List.
1- Create event Receiver for ERsDef and the event sources are; Item Updateting and Item Updated with the following code;
public override void ItemUpdating(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
if (properties.ListTitle == "Event Receivers")
{
int timerJobID = (properties.AfterProperties["TimerJobID"] != null) ? int.Parse(properties.AfterProperties["TimerJobID"].ToString()) : 0;
if (timerJobID > 0)
{
switch (timerJobID)
{
case 1:
AddTask(properties.OpenWeb());
break;
case 2:
UpdateFirstTask(properties.OpenWeb());
break;
default:
break;
}//end switch
}//end if (timerJobId > 0)
}//end if (properties.ListTitle == "Event Receivers")
}//end function
public override void ItemUpdated(SPItemEventProperties properties)
{
base.ItemUpdated(properties);
if (properties.ListTitle == "Event Receivers")
{
//get timerJob ID
int timerJobID = (properties.AfterProperties["TimerJobID"] != null) ? int.Parse(properties.AfterProperties["TimerJobID"].ToString()) : 0;
//get the item that should be updated in WF list
SPWeb web = properties.OpenWeb();
SPSite site = web.Site;
SPList WFsList = web.Lists["Workflows"];
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name='TimerJobID'/><Value Type='Text'>" + timerJobID.ToString() + "</Value></Eq></Where>";
SPListItem WFListItem = null;
SPListItemCollection WFListItems = WFsList.GetItems(query);
if (WFListItems.Count > 0)
{
WFListItem = WFListItems[0];
//update list item in the WF list
web.AllowUnsafeUpdates = true;
WFListItem["LastTimeRan"] = DateTime.Now.ToString(); ;
EventFiringEnabled = false;
WFListItem.Update();
EventFiringEnabled = true;
web.AllowUnsafeUpdates = false;
//find and start the workflow that is associated to this list item
var assoc = WFsList.ContentTypes["WFsCT"].WorkflowAssociations.GetAssociationByName("TimerJobWF-Assoc", CultureInfo.InvariantCulture);
if (assoc != null)
{
var result = site.WorkflowManager.StartWorkflow(WFListItem, assoc, string.Empty);
}
}//end if (WFListItems.Count > 0)
}//end if (properties.ListTitle == "Event Receivers")
}//end function
private void AddTask(SPWeb web)
{
SPSite site = web.Site;
SPList TasksList = web.Lists["Tasks"];
SPListItem TaskItem = TasksList.Items.Add();
web.AllowUnsafeUpdates = true;
TaskItem["Title"] = "New task from the timer Job";
EventFiringEnabled = false;
TaskItem.Update();
EventFiringEnabled = true;
web.AllowUnsafeUpdates = false;
}
private void UpdateFirstTask(SPWeb web)
{
SPSite site = web.Site;
SPList TasksList = web.Lists["Tasks"];
if (TasksList.Items.Count > 0)
{
SPListItem TaskItem = TasksList.Items[0];
web.AllowUnsafeUpdates = true;
TaskItem["Title"] = "Ran at " + DateTime.Now.ToString ();
EventFiringEnabled = false;
TaskItem.Update();
EventFiringEnabled = true;
web.AllowUnsafeUpdates = false;
}
}
Top
12- Deploy the solution.
13- Very Important , go to the workflow again and re-write the action Update Event Receivers. Every time you deploy the sandbox solution, you will need to re-write this action again.
14- Associate the Workflows list to the workflow TimerJobWorkFlow that has been created in SharePoint Designer. Name the association “TimerJobWF-Assoc”. Make the workflow starts manually and on change as show below.
15- In Workflows list, select first item, click workflows in ribbon, start the workflow, enjoy.