Showing posts with label WF. Show all posts
Showing posts with label WF. Show all posts

Wednesday, November 18, 2009

Custom Workflow in Project Server 2007 - Part 4

Part 1
Part 2
Part 3


Writing Codes for Activities

Add the following using directives to the top of the class file, If already not there:

using Microsoft.SharePoint.Workflow;
using System.Collections.Specialized;
using System.Text;
using Microsoft.SharePoint;


Add the following class member fields :

public string m_ProjectName;
public bool m_LoginSuccessfull;
public int m_PublishRequestUserId;


OnWorkflowActivated1_Invoked - This is the first method to be called in the workflow execution. Add the following code to initialize some variables using the workflowProperties field (extracting properties from the list item that initiated the workflow).

private void OnWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
{
workflowId = workflowProperties.WorkflowId;
m_ProjectGuid = new Guid((string)workflowProperties.Item
[EPMWFConstants.CON_ProjectGuidColumn]);
m_ProjectName = (string)workflowProperties.Item
[EPMWFConstants.CON_ProjectTitleColumn];
string userToken = (string)workflowProperties.Item
[EPMWFConstants.CON_PublishRequestorColumn];
m_PublishRequestUserId = Convert.ToInt32(userToken.Substring(0,userToken.IndexOf(";")));
}


SendRequestsForApproval_Invoking - Set up the email fields for the request to approvers.

private void SendRequestsForApproval_Invoking(object sender, EventArgs e)
{
m_MailToHeader = new StringDictionary();
StringBuilder returnString = new StringBuilder();
SPGroup approverGroup = workflowProperties.Web.SiteGroups[EPMWFConstants.CON_ApproversGroupName];
foreach (SPUser currentApprover in approverGroup.Users)
{
if (!string.IsNullOrEmpty(currentApprover.Email))
{
returnString.AppendFormat("{0};", currentApprover.Email);
}
}
m_MailToHeader.Add("To", returnString.ToString());
m_MailToHeader.Add("Subject", string.Format("Project publishing approval request : \"{0}\"", m_ProjectName));
StringBuilder mailBody = new StringBuilder();

mailBody.AppendFormat("{0} has requested approval to publish the Project" +
"\"{1}\".
", workflowProperties.Web.Users.GetByID(m_PublishRequestUserId).Name, m_ProjectName);
m_MailToBody = mailBody.ToString();
}


SendPendingApproval_Invoking - Send pending notices to publishers

private void SendPendingApproval_Invoking(object sender, EventArgs e)
{
m_MailToHeader = new StringDictionary();
m_MailToHeader.Add("To", workflowProperties.Web.Users.GetByID(m_PublishRequestUserId).Email);
m_MailToHeader.Add("Subject", string.Format("Project \"{0}\" submitted for approval", m_ProjectName));
m_MailToBody = "The project has been submitted for " +
"approval. You will be notified when the item has been approved/rejected.";
}


WhileWaitingForApproval_Condition - Set Result to true if the status on the list item is set to Waiting for Approval,which causes the loop to continue.

private void WhileWaitingForApproval_Condition(object sender, ConditionalEventArgs e)
{
e.Result = (string)workflowProperties.Item[EPMWFConstants.CON_ApprovalStatusColumn] == EPMWFConstants.CON_StatusWaitingText;
}


IfApproved_Condition - checks whether the item is approved Else assumes that the item has been rejected.

private void IfApproved_Condition(object sender, ConditionalEventArgs e)
{
e.Result = (string)workflowProperties.Item[EPMWFConstants.CON_ApprovalStatusColumn] == EPMWFConstants.CON_StatusApprovedText;
}


PsiQueuePublish_Invoking - code for the actual web service call
to the Queue System PSI.

private void PsiQueuePublish_Invoking(object sender, InvokeWebServiceEventArgs e)
{
//Create a new Job Guid for this operation
m_PublishJobGuid = Guid.NewGuid();
//Get the web service proxy from the activity
EPMWF.psiProjects.Project webService = (EPMWF.psiProjects.Project)e.WebServiceProxy;
//use the default credentials
webService.UseDefaultCredentials = true;
//Set the URL to the web service
webService.Url = workflowProperties.WebUrl + "/_vti_bin/PSI/Project.asmx";
//Set an empty string for the publish wss url
m_PublishWssUrl = string.Empty;
}


SendApproval_Invoking - Send an Approved notification to requester.

private void SendApproval_Invoking(object sender, EventArgs e)
{
string subject = "Publishing of Project \"{0}\" has been APPROVED";
string body = "Publishing of the project \"{0}\" has been approved.";
m_MailToHeader = new StringDictionary();
m_MailToHeader.Add("To", workflowProperties.Web.Users.GetByID(m_PublishRequestUserId).Email);
m_MailToHeader.Add("Subject", string.Format(subject, m_ProjectName));
m_MailToBody = string.Format(body, m_ProjectName);
}


SendRejected_Invoking - Send an rejected notification to requester.

private void SendRejected_Invoking(object sender, EventArgs e)
{
string subject = "Publishing of Project \"{0}\" has been REJECTED";
string body = "Publishing of the project \"{0}\" has been rejected.";
m_MailToHeader = new StringDictionary();
m_MailToHeader.Add("To", workflowProperties.Web.Users.GetByID(m_PublishRequestUserId).Email);
m_MailToHeader.Add("Subject", string.Format(subject, m_ProjectName));
m_MailToBody = string.Format(body, m_ProjectName);
}


Deploying the Solution

  • Sign assembly with a strong name key & install in the Global Assembly Cache

  • Workflow registration with WSS - create and register a feature that exposes the custom workflow.

    • Create following dir ,sub dir & files in your Project/Solution dir
      \TEMPLATE\FEATURES\EPMWF

    • Add two xml files , feature.xml & workflow.xml in this sub dir

    • open the feature.XML file and add the following:

      <?xml version="1.0" encoding="utf-8" ?>
      <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
      Id="{guid}" Title="EPM Workflow Sample" Scope="Site" Hidden="False" Version="1.0.0.0">
      <ElementManifests><ElementManifest Location="workflow.xml" /></ElementManifests>
      <Properties><Property Key="GloballyAvailable" Value="true" /></Properties>
      </Feature>

      Create a new GUID and replace the {guid}tag.

    • open the workflow.xml file and add the following:

      <?xml version="1.0" encoding="utf-8" ?>
      <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <Workflow Id="{guid}" Name="Publishing Approval" Description="EPM Publishing Approval" CodeBesideClass="EPMWF.PublishingWorkflow" CodeBesideAssembly="EPMWF, Version=1.0.0.0, Culture=neutral, PublicKeyToken={token} " >
      <Categories />
      <MetaData><StatusPageUrl>_layouts/WrkStat.aspx</StatusPageUrl></MetaData>
      </Workflow>
      </Elements>

      Replace the {token} tag with the public key token of your assembly.
      Create a new GUID and replace the {guid}tag.

    • Install and activate this feature on your PWA site
      %STSADM% -o InstallFeature -filename EPMWF\feature.xml –force
      %STSADM% -o activatefeature -name EPMWF -url http://server/PWA

    • Attach the workflow to the custom list - browse to the Workflow Settings page. Select Publishing Approval Workflow ,set Name field,task and history lists. Finally, select Start This Workflow When a New Item Is Created under Start Options.


  • Registering the server-side event handler with Project Server 2007-

    • Browse to the ‘Server-Side Event Handler Configuration’ page link.
    • select the Publishing event for Project group, and click the link.
    • Add a new handler by clicking the New Event Handler link.
    • Enter a name such as ‘EPM Publishing Workflow’ and a description.
    • Then enter the assembly name - name, Version=1.0.0.0, Culture=neutral, PublicKeyToken={token}
    • Enter the class name - .
    • enter 1 as the order of the handler.
    • Click the Save button to register the event handler

    For More details on Project server Event Handler check this article .


For testing Create a new project using Project Professional or Create a Proposal using PWA site and publish it.

Thursday, November 12, 2009

Custom Workflow in Project Server 2007 - Part 3

Part 1
Part 2



Step 4 - Create a custom workflow


For More details on WSS workflow check this article .


Open the Sequential Workflow file (PublishingWorkflow.cs) in design mode and add following activites -
  1. OnWorkFlowActivated activity

  2. Sequence activity : Rename this activity as 'PendingNotfications' and add following child activites -
    • SendEmail - Rename this activity as 'SendRequestForApproval'

    • SendEmail - Rename this activity as 'SendPendingNoticeToPublisher'

  3. While activity – Rename this activity as 'WhileWaitingForApproval' and add following child activities.
    • OnWorkflowItemChanged activity

  4. IfElse activity
    • First branch : Add following child activities -
      • InvokeWebService activity – Rename this activity as 'EPMProjects'.Add the web reference as -
        Reference name – psiProjects ,
        URL = http://server/pwa/_vti_bin/psi/project.asmx

      • SendEmail activities - Rename this activity as 'SendApprovalEmail'

    • Second Branch : Add following child activities -
      • SendEmail activities - Rename this activity as 'SendRejectedEmail'


The final design view should look like this -


The red exclamation icon in the upper-right corner of activities icon indicates that a required property must be set.Clicking the icon displays the list.

Now open the PublishingWorkflow.cs file in code view and add following using statement & class members -

  • using EPMWF.psiProjects; //NameSpace.Psi Web Refrence Name

  • public Guid workflowId = default(System.Guid);
    public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();


Set Activity Properties
Open the workflow file in design view and set the required properties of each activity as mentioned below -
  • Activity - OnWorkFlowActivated1
    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow
      WorkflowProperties\Name = PublishingWorkflow
      WorkflowProperties\Path = workflowProperties

    • Invoked = OnWorkflowActivated1_Invoked
      After You Enter method name here, VS.net will jump to code with a method signature created.

  • Activity = SendRequestForApproval
    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow

    • Body - select the Body row and click the ellipsis.Click Bind a New member field.enter name = m_MailToBody ; Select Create field option, click Ok .Verify Name and Path
      Body\Name = PublishingWorkflow
      Body\Path = m_MailToBody

    • Headers - Create field in the same as done for Body.Name =m_MailToHeader.The Headers property is a hash where the Subject and To fields can be specified
      Headers\Name = PublishingWorkflow
      Headers\Path = m_MailToHeader

    • MethodInvoking = SendRequestsForApproval_Invoking
      After You Enter method name here, VS.net will jump to code with a method signature created.

  • Activity = SendPendingNoticeToPublisher
    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow
      Body\Name = PublishingWorkflow
      Body\Path = m_MailToBody
      Headers\Name = PublishingWorkflow
      Headers\Path = = m_MailToHeader
      MethodInvoking = SendPendingApproval_Invoking



  • Activity = WhileWaitingForApproval
    • Condition = Code Condition
    • Condition = WhileWaitingForApproval_ Condition
      After You Enter method name here, VS.net will jump to code with a method signature created.


  • Activity = OnWorkflowItemChanged
    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow


  • Activity = ifElseBranchActivity1
    • Condition =Code Condition
      Condition = IfApproved_Condition


  • Activity = EPMProjects
    • MethodName =QueuePublish

    • Create fields for following properties in the same way as in Body
      ReturnValue = m_PublishResult
      JobUid = m_PublishJobGuid
      ProjectUid = m_ProjectGuid
      WssUrl = m_PublishWssUrl

    • FullPublish=True

    • Invoking =PsiQueuePublish_Invoking
      VS.net will jump to code with a method signature created.


  • Activity = SendApprovalEmail
    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow
      Body\Name = PublishingWorkflow
      Body\Path = m_MailToBody
      Headers\Name = PublishingWorkflow
      Headers\Path = = m_MailToHeader
      MethodInvoking = SendApproval_Invoking



  • Activity = SendRejectedEmail

    • Correlation Token = publishWorkflowToken
      Correlation Token\OwnerActivityName = PublishingWorkflow
      Body\Name = PublishingWorkflow
      Body\Path = m_MailToBody
      Headers\Name = PublishingWorkflow
      Headers\Path = = m_MailToHeader
      MethodInvoking = SendRejected_Invoking




After setting all these properties Visual studio will automatically create some member declaration & empty method signatures for you. The code file will look like this...


namespace EPMWF
{
public sealed partial class PublishingWorkflow: Microsoft.SharePoint.WorkflowActions.SharePointSequentialWorkflowActivity
{
public PublishingWorkflow()
{
InitializeComponent();
}

public Guid workflowId = default(System.Guid);
public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();

public String m_MailToBody = default(System.String);
public System.Collections.Specialized.StringDictionary m_MailToHeader = new System.Collections.Specialized.StringDictionary();

private void OnWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e){}

private void SendRequestsForApproval_Invoking(object sender, EventArgs e){}

private void SendPendingApproval_Invoking(object sender, EventArgs e){}

private void WhileWaitingForApproval_Condition(object sender, ConditionalEventArgs e)
{}

private void IfApproved_Condition(object sender, ConditionalEventArgs e){}

public Guid m_ProjectGuid = default(System.Guid);
public ProjectRelationsDataSet m_PublishResult = new EPMWF.psiProjects.ProjectRelationsDataSet();
public Guid m_PublishJobGuid = default(System.Guid);
public String m_PublishWssUrl = default(System.String);

private void PsiQueuePublish_Invoking(object sender, InvokeWebServiceEventArgs e){}

private void SendRejected_Invoking(object sender, EventArgs e){}

private void SendApproval_Invoking(object sender, EventArgs e){}
}
}


Part 4 - Writing Codes for Activities

Friday, November 6, 2009

Custom Workflow in Project Server 2007 - Part 2

For implementing this Workflow , we need to do following -
  1. Create a WSS Custom list.
  2. Create a Sharepoint user group for Approvers.
  3. Create a server side project publishing event handler.
  4. Create a custom workflow using Workflow Foundation (WF) and attach it to WSS list created in step 1.
  5. Deploy the solution


Step 1- Create a WSS Custom list
Create a WSS custom list at the root web of Project Web Access 2007 site.
  • Browse to the root Project Web Access 2007 site. http://server/pwa
  • Create a custom list(Site Actions ->Create) on root web – Name = Project Publishing;
  • Add following columns in 'Project Publishing' list -
    • Title = Single line of text – Holds the project name.
    • ProjectGuid = Single line of text = Holds the project ID that this item represents.
    • Status = Choice = Drop-down list for the approval status for this item. The choices are - Waiting for Approval, Rejected, and Approved.
    • Requester = Person or Group = Lists the user who requested the project publish.



Step 2 - Create a Sharepoint user group
Create the Publishing Approvers Sharepoint group on Project Web Access 2007 site.
  • Browse to the root Project Web Access 2007 site. http://server/pwa
  • Create the Publishing Approvers Sharepoint group(Site Actions - Site Settings -People and Groups -New menu -New Group) - Name= 'Publishing Approvers'
  • Add individual users in the group (New Menu -Add Users)


Step 3 - Create server side event receiver
We will create a single visual studio solution for step3 (Event Handler) & step 4 (Workflow).
  • Open Visual Studio .net 2005 and create a new project using 'Sequential Workflow Library template' - Name = EPMWF

  • Add following Project server & Sharepoint assembly references -

    Microsoft.Office.Project.Server.Events.dll
    Microsoft.Office.Project.Server.Library.dll
    Microsoft.SharePoint.dll
    Microsoft.SharePoint.Library.dll
    Microsoft.SharePoint.Security.dll
    Microsoft.SharePoint.WorkflowActions.dll

  • For Project Server dlls.Select the Browse tab in Add reference dialog and browse to the folder containing the Microsoft Office Project Server assemblies. Select them and click OK.
  • Delete Workflow1 file from Visual studio solution, which is created by default

  • Add following items in the visual studio project
    1. EPMWFConstants.cs - A static class that will hold constants needed by custom workflow & Event Handler.
    2. PublishingEventHandler.cs - A class file for handling project publishing event.
    3. PublishingWorkflow.cs - A Sequential Workflow item by Right click the project, select Add, and click on Sequential Workflow.

  • Coding EPMWFConstants.cs - This file will contain a static class that will hold constants needed for interfacing to WSS by the custom workflow & Event Handler.These constants will be string constants that will hold the custom list and columns details & Approver Group details. Add following constant in this class

    public const string CON_PublishListTitle = "Project Publishing";
    public const string CON_ProjectTitleColumn = "Title";
    public const string CON_ProjectGuidColumn = "GUID0";
    public const string CON_ApprovalStatusColumn = "Status";
    public const string CON_PublishRequestorColumn = "Requester";

    public const string CON_StatusApprovedText = "Approved";
    public const string CON_StatusRejectedText = "Rejected";
    public const string CON_StatusWaitingText = "Waiting for Approval";

    public const string CON_ApproversGroupName = "Publishing Approvers";


  • Coding PublishingEventHandler.cs - The event handler method first checks whether a list item for the project being published has already been created. If an item exists, the publish is allowed to proceed only if it has been approved. If the item does not exist, a new item is added, which initiates the custom workflow.

    • Check this for the structure of the class file for Event Handler.

    • Add following code in 'OnPublishing' method

      public override void OnPublishing(PSContextInfo contextInfo, ProjectPrePublishEventArgs e)
      {
      //Open PWA site Object
      using (SPSite site = new SPSite(contextInfo.SiteGuid))
      {
      //Open root web object
      using (SPWeb web = site.OpenWeb())
      {
      //create an object of the Publishing Approval custom list
      SPList list = web.Lists[EPMWFConstants.CON_PublishListTitle];

      string projectGuid = e.ProjectGuid.ToString("N").ToUpper();
      SPQuery query = new SPQuery();
      query.Query = "" + projectGuid + "";
      SPListItemCollection items = list.GetItems(query);
      SPListItem item;
      if (items == null items.Count == 0)
      {
      //Create a new list item
      item = list.Items.Add();
      item[EPMWFConstants.CON_ProjectGuidColumn] = projectGuid;
      item[EPMWFConstants.CON_ProjectTitleColumn] = e.ProjectName;
      item[EPMWFConstants.CON_ApprovalStatusColumn] = EPMWFConstants.CON_StatusWaitingText;
      item[EPMWFConstants.CON_PublishRequestorColumn] = web.Users[contextInfo.UserName];
      item.Update();
      }
      else
      {
      item = items[0];
      }

      //Cancel the publish event if the item isn’t approved
      switch ((string)item[EPMWFConstants.CON_ApprovalStatusColumn])
      {
      case EPMWFConstants.CON_StatusRejectedText:
      e.CancelReason = "Publishing for this project has been rejected.";
      e.Cancel = true;
      break;
      case EPMWFConstants.CON_StatusWaitingText:
      e.CancelReason = "Publishing for this project is pending approval.";
      e.Cancel = true;
      break;
      }
      }
      }
      }


For More details on Project server Event Handler check this article .


Part-3 : Create a custom workflow

Tuesday, November 3, 2009

Custom Workflow in Project Server 2007 - Part 1



Workflow is a powerful way to guide business processes. As Project Server 2007 is hosted in WSS , so by default it allow us to create workflow for WSS object such as Document Library , Lists . Project Server 2007 does not provide us an ability to build direct workflows around Project Server objects. For example there may be some business need to have an workflow on server side process e.g. workflow on project Publishing.
We can accomplish this with the help of a server-side event handler & a WSS list workflow. A server side event listener can capture the event for that process and cancel it. It then kicks off a workflow in WSS by altering a list item. After a workflow condition has been met, the workflow can re execute the operation.

For exploring the workflow in EPM , we will take an example of a business process (Project Publishing Workflow ) where an approval is required on project publishing. Project managers are required an approval from a program manager or a Delivery Manager before publishing any project. In this scenario the workflow steps may be look like this-

  1. Project Manager Creates a Project and send an approval request email to Program manger

  2. Program manager will then analyze the schedule and Approve/reject the request. And send an notification to requestor

  3. In case of approve , project manager will publish the project.



With the help of an project server event handler , a WSS list workflow & Project server Interface(PSI), we can create an automated workflow which will accomplish the above task in following steps

  1. Project manager create and publish a project

  2. An event handler ,attached to a Project Publishing server side event, will cancel the process and add an item in a WSS list.

  3. WSS list item kickoff a custom workflow (Automatic workflow on item creation)

  4. The workflow will send an notification to Approver, and wait for the status change

  5. Approver then open the list item and change the status to Approve.

  6. Once the status field is changed , The workflow then Publish the project through the project server 2007 web services(PSI) and send an notification to requestor.







Implementation : Part 2