Tutorial Share Point Happy Birthday Workflow Slideshare


Published on

Tutorial on creating a SharePoint workflow that sends a Happy Birthday card (as a content type) that is paritally populated using an InfoPath form. It is then emailed to the recipient of the card. The interesting part is the disabling of the InfoPath form once the workflow is completed by using a secondary data source.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Tutorial Share Point Happy Birthday Workflow Slideshare

  1. 1. SlideShare.comSeptember 2009Tutorial – Coding a SharePoint Happy Birthday Workflow with Visual Studio 2008Using an InfoPath Form for Input and Word Document as OutputGeorge Catrombon<br />Contents TOC o " 1-3" h z u SUMMARY PAGEREF _Toc241566766 h 3DESCRIPTION OF THE WORKFLOW PAGEREF _Toc241566768 h 3SUMMARY SNAPSHOTS OF OUR WORKFLOW PAGEREF _Toc241566769 h 4OVERVIEW OF THIS TUTORIAL PAGEREF _Toc241566770 h 12STEPS INVOLVED IN THE PROCESS PAGEREF _Toc241566771 h 12SOFTWARE REQUIRED TO DUPLICATE THIS PROJECT PAGEREF _Toc241566774 h 13STEP 1 – CREATE A NEW LIBRARY IN SHAREPOINT PAGEREF _Toc241566775 h 13STEP 2 – CREATE A TEMPLATE FOR THE BIRTHDAY CARD PAGEREF _Toc241566776 h 17STEP 3 – CREATE THE WORD DOCUMENT FOR THE BIRTHDAY CARD FROM WITHIN THE LIBRARY PAGEREF _Toc241566779 h 20STEP 4 – CREATE OUR INFOPATH 2007 FORM TO ACCEPT INPUT FROM THE USERS. PAGEREF _Toc241566787 h 35STEP 5 – CODE OUR WORKFLOW IN VISUAL STUDIO PAGEREF _Toc241566794 h 70STEP 6 – CONFIGURE WORKFLOW.XML AND FEATURE.XML IN VISUAL STUDIO PAGEREF _Toc241566798 h 92CONCLUSION PAGEREF _Toc241566799 h 95<br />SUMMARY<br />This tutorial will demonstrate how to create a SharePoint sequential workflow that will reveal many useful techniques that can be applied in the field. The purpose of this workflow is to send a Birthday Card (as a Word document) via Email to a SharePoint user with some of the text of the Birthday Card filled out dynamically in C# code during the workflow. The dynamically populated fields will get their input from an InfoPath form presented to the person who is assigned the task of signing the card. This is similar to what might happen in an office today when your supervisor wants a person in his/her group to send a birthday card to a particular birthday boy or girl. In our situation, the supervisor creates the card from a SharePoint library as a Word document (as a content type) which has a workflow attached to it. The difference here is that the process is under the management of the workflow which we will code in Visual Studio 2008.<br />Note: This is a long tutorial with a vast number of steps required to complete it. The reader should be alert to how complex the process is to create a very simple sequential workflow that does some fancy work behind the scenes using InfoPath forms. <br />DESCRIPTION OF THE WORKFLOW<br />Let’s take a moment to better describe what we want. A birthday card will be created as a content type for a SharePoint library that we will create. We will call the library Happy Birthday Card Library. A birthday card will be a Word document, with a picture of a birthday cake, and several pre-defined fields as Word properties which are linked to column types in the library. Some of these fields will be filled out by the initiator of the birthday card (the supervisor most likely) which will include the name of the recipient (E.g. “Jane Smith”), an opening greeting (E.g. “Best wishes on your Birthday!”), the email address of the recipient, and the login name of the person assigned the task of signing the card (E.g. JohnDoe@yourdomain.com). There will then be two additional fields which need to be populated by the signer of the card. These two fields are filled out by using an InfoPath form. These fields are from (E.g. “John”) and the message the signer would like to include (E.g. “Hope you like the cake!”). When the signer completes the workflow task, the workflow will finish populating the Word document Birthday Card with the two fields obtained from the InfoPath form, and then email the updated Birthday Card to the recipient as an email attachment. The workflow will then mark the task as completed. The other thing we will do is disable the submit button on the InfoPath form when the workflow task is completed. If we did not do this, then the user could re-submit the form again and again from the Tasks page. There will have to be a feedback mechanism for the InfoPath form to make it aware that the task was completed. We disable the submit button in this example.<br />SUMMARY SNAPSHOTS OF OUR WORKFLOW<br />For reference, the snapshots below summarize the procedure of sending the birthday card.<br /><ul><li>Administrator creates a Birthday Card (Figure 1)</li></ul>Figure 1<br /><ul><li>Administrator fills out 4 properties of the Word document (Figure 2)</li></ul>Figure 2 – Creator fills out 4 fields in the Word properties boxes<br />Note that Mary Smith was assigned the task of signing the card (E.g. Assigned To Login Name = MY_NETBIOS_NAMEmarysmith in the property field Figure 2)<br /><ul><li>Exit Word (Figure 3)</li></ul>Figure 3 - Exit Word<br /><ul><li>Birthday Card is saved in the SharePoint Library (Figure 4)</li></ul>Figure 4 <br />Workflow is stated automatically on save. ( Figure 5)<br />Figure 5 – Our workflow has started.<br />We now sign in as Mary Smith, signer of the card. The task is present on her task list. (Figure 6)<br />Figure 6 – We are now signed in as Mary Smith<br />Mary clicks “Please sign this birthday card” and gets the InfoPath form which she fills out 2 fields. (Figure 7)<br />Figure 7 – Mary fills out the form.<br />Mary clicks the submit button and the task completes. (Figure 8)<br />Figure 8 – Task completed!<br />Recipient of the card gets an email with the filled out Birthday Card. (figure 9 & 10)<br />Figure 9<br />Figure 10<br /> Word document is fully populated by our Workflow. (Figure 11)<br />Figure 11 – Card filled out by the Workflow!<br /><ul><li>Notice that we disable the Submit button in the InfoPath form if Mary tries to fill it out a 2nd time. (Figure 12).</li></ul>Figure 12 – Submit button is disabled via feedback from the Workflow on subsequent tries<br />So, that’s the flow. It’s very simple from the user’s perspective. However, to get all this working requires a considerable amount of knowledge and work. Let’s get started.<br />OVERVIEW OF THIS TUTORIAL<br />This tutorial has a huge number steps that need to be performed to set up this workflow. There is very little C# code in the Workflow, and most of the work is done outside of the workflow programming in preparation. Also, this tutorial is quite long since there are so many pieces that need to be discussed to get all of this to work together. Also, there are some issues that would need to be addressed in a production environment. One of them is that we only have one Word document Birthday Card in our SharePoint library. Our C# code references this Word document by its filename and is hard coded. We would need to enhance the C# code to allow any filename to be used for a Birthday Card. One way to do this is to include another property on the Birthday Card Word document named Filename which the initiator of the card would fill out. Another issue is that our InfoPath form takes over the page where a user would normally change the % complete and status of the Workflow. That is why we update the completed status in the Workflow rather than let the user do it manually. Students of SharePoint Minds are encouraged to modify this Workflow to address those issues. <br />In any event, this tutorial has a wealth of good information regarding SharePoint workflows that would be helpful. <br />STEPS INVOLVED IN THE PROCESS<br />To create this work flow, we have to perform the following steps:<br /><ul><li>Create a new library in SharePoint named Happy Birthday Card Library.
  2. 2. Create a template for the Birthday Card in the library by defining new columns.
  3. 3. Create the Word document Birthday Card.
  4. 4. Add the Birthday Card as a content type to the library.
  5. 5. Create our InfoPath 2007 task form to accept user input for the From and Message fields.
  6. 6. Configure the InfoPath form to receive feedback from the workflow by specifying a secondary data source.
  7. 7. Configure the InfoPath form to be presented in a web page with Full Trust security and temporary certificate.
  8. 8. Code the Workflow using Visual Studio 2008 in C#.
  9. 9. Configure the Workflow in Visual Studio to include the InfoPath form as part of the deployment.
  10. 10. Configure the Workflow.xml file so that our InfoPath form is presented to the user in a web page.
  11. 11. Configure feature.xml so that our InfoPath form is deployed as a feature.</li></ul>NOTE: It should be noted that this author created this library under the Team Site tab of his main portal SharePoint site. This means that the columns that we create (E.g. To, From, Greeting, Assigned to Login Name, Email Address, Completed, and Message) in our Happy Birthday Library are not global site columns, but local to the Team Site. <br />SOFTWARE REQUIRED TO DUPLICATE THIS PROJECT<br />The software requirements for this project are significant. The software that you will need is as per the following list:<br /><ul><li>Microsoft ASP.NET 3.5 Framework
  12. 12. Microsoft Windows Server 2003 installed on the Virtual Machine (E.g. the VM)
  13. 13. Ability of the virtual machine to connect to the internet using the Microsoft Loopback adapter (see the document on this topic by this author on the SharePointMinds.com site).
  14. 14. Microsoft SharePoint 2007
  15. 15. Microsoft SQL Server 2005 (required by SharePoint)
  16. 16. Microsoft Visual Studio 2008
  17. 17. Windows SharePoint Services 3.0 Tools: Visual Studio 2005 Extensions, Version 1.2
  18. 18. InfoPath Forms 2007
  19. 19. Visual Studio Tools for Applications 2007
  20. 20. Microsoft Word 2007</li></ul>STEP 1 – CREATE A NEW LIBRARY IN SHAREPOINT<br />On your SharePoint site (in this case, the authors Team Site), click Site Actions > Create as shown in Figure 13. <br />Figure 13<br />On the resulting page, click Document Library as shown in Figure 14.<br />Figure 14<br />Fill out the form for the new library and Click Create. Refer to Figure 15.<br />Figure 15<br />This will result in a new library as shown in Figure 16.<br />Figure 16<br />This completes the creation of our library.<br />STEP 2 – CREATE A TEMPLATE FOR THE BIRTHDAY CARD<br />In this step, we are going to create several new columns. These columns will be available in the Happy Birthday Card Word document that we create and will become Word document objects. SharePoint will take care of the linking for us. We will be able to access the contents of the Word objects in the C# code behind. That means that we can read the fields, but it also means that we can write to the fields in the C# code behind in the Workflow. Therefore, the Word document serves as an input form where we can enter values in the fields. It is intended that only 4 of the fields are filled out when the administrator creates a new Birthday Card (E.g. To, Greeting, Assigned To Login Name, Email Address). The remaining fields will be filled out in C# code using data from the InfoPath form. <br />In the Happy Birthday Card Library, click Settings > Document Library Settings as shown in Figure 17.<br />Figure 17<br />Now we want to create some new columns for our Birthday Card. We are going to have to repeat the process for each of our columns. Column names in red are required fields to be filled out by the creator of the card. Our columns are:<br />To<br />Greeting<br />From<br />Message<br />Email Address<br />Assigned To Login Name<br />Completed<br />The first four columns will be filled out by the person who starts the Birthday Card process from the library. That is, when he/she creates new instance of the Birthday Card by clicking the New > Happy Birthday Card menu selection in the library. <br />To: will be the name of the person who will receive the Birthday Card. Example: To: Jane Smith <br />Initial Greeting: The second line will be the initial greeting which is the basic text of the card to be displayed. Example: Happy Birthday Jane!<br />Email Address: The email address of the recipient of the card.<br />Assigned To Login Name: The ID of the person assigned the task of signing the card and providing a message. Example: yourdomainjohnsmith<br />The remaining columns will be filled out dynamically by the workflow that we will code. The content of the remaining columns will be obtained from information that is submitted when the signer completes their workflow task by filling out an InfoPath 2007 form during the workflow process. <br />So now let’s add come columns. We will only show the process for the first column. The rest of the columns will follow the same procedure.<br />Click Create Column as shown in Figure 18. <br />Figure 18<br />The resulting page is shown in Figure 19. Fill out the form as shown in Figure 19. <br />NOTE: All the fields will be single line of text for all our columns. Also, four of the fields will be required fields, so check the radio button to make it a required field. Recall that we denoted the required columns in red above.<br />Figure 19<br />Repeat this process for our remaining columns. The final result showing all columns created should appear as shown in Figure 20 (except for title which was there already).<br />Figure 20 – Our 7 columns added<br />STEP 3 – CREATE THE WORD DOCUMENT FOR THE BIRTHDAY CARD FROM WITHIN THE LIBRARY<br />Return to the Birthday Card Library in SharePoint. From the New > New Document menu selection, create a new Word document (which will be aware of our columns template that we just created). Refer to Figure 21.<br />Figure 21<br />An instance of a blank Word document will appear and will show our columns template as shown in Figure 22. <br />Figure 22 – Note the text boxes, 4 with red asterisks denoting required fields<br />Next, convert the Word document as shown in Figure 23 and Figure 24. <br />NOTE: You must convert the document or you will not be able to create the objects that are linked to the 7 site columns that we created.<br />From the main menu, click Convert. A dialog will appear as in Figure 24. Click OK. <br />Figure 23<br />Figure 24<br />Now we must save the document to a directory on disk. Do not save the document in the SharePoint library which is the default. Save it to a directory of your choice on disk. To save the document, this author navigated to his My Documents directory as shown in Figure 25. <br />Figure 25<br />Next, we added a Happy Birthday text at the top and added a picture as shown in Figure 26. Once you have done this, save the document (again).<br />Figure 26 – We added a picture and Happy Birthday as text<br />Now we need to add some Word Objects to our document in the body just below the picture of the cake. We will perform the following procedure for all of our columns in turn. <br />Place the cursor in Word where you would like to create the To object (just below the cake for this one). Then, Click the Insert tab, then Quick Parts > Document Property. Refer to Figure 27. <br />Figure 27 – An object corresponding to the menu item To will be placed at the cursor.<br />On the resulting dropdown, scroll down to the To listing and click it. This will cause the To object to be inserted at the cursor in the Word document as shown in figure 28.<br />Figure 28 – Our To object was inserted at the cursor in Word<br />NOTE: These objects in Word will be available in our C# code behind in the workflow. We can either read from them, or write to them in code.<br />Perform these steps again in turn, for our remaining objects. When finished, the Word document should appear as in Figure 29.<br />Figure 29 – 4 of 7 of our template columns are now objects in the body of the Word document. We never show the other 3 properties on the card.<br />Our Word document is now finished. Click save (remember not to save in the SharePoint library, save it to the same directory you have been using), and then exit Word by clicking Exit Word as shown in Figure 30.<br />Figure 30 – Exit Word<br />Figure 31 – Saving the completed document<br />We now need to add our Happy Birthday Word document to our library. To do that, navigate to our Happy Birthday Library, and click Actions > Open in Windows Explorer as shown in Figure 32. <br />Figure 32<br />When explorer opens, click on the Forms icon, and then you should see the various forms as shown in Figure 33. We need to copy and paste our Happy_Birthday_Card.docx file to this directory. To do that, navigate to the location where you saved the Word document, copy it, and navigate back to this directory and paste it. The result should be as shown in Figure 34. <br />Figure 33 – We need to add our Word document here<br />Figure 34 – Are new Word document pasted into the directory<br />Next, click on Settings > Document Library Settings as shown in Figure 35. <br />Figure 35<br />We now need to add the content type for our Happy Birthday Card Word document to the library. As shown in Figure 36, click on Add from existing content types.<br />Note: if your page does not show the Content Types section, refer to Appendix A to get it to display. <br />Figure 36<br />Now, using Figure 37, find the content type Birthday Card in the left hand list box, and add it to the right hand list box. <br />Figure 37<br />Note: The content type of Birthday Card should appear in the left hand list box because we placed our Word document in the Forms directory of the library. Your name may differ depending on what you saved the Happy Birthday Card Word document filename to.<br />We should now see a new content type in our library as shown in Figure 38.<br />Figure 38<br />NOTE: this author recommends that you change the order of the content types shown in Figure 38. Place the Birthday Card content type first, and move Document below it. You can reverse the order by clicking the link just below the red circle in Figure 38 (its cut off in the figure) called Change new button order and default content type.<br />Next, we need to specify the template to use for the Birthday Card content type. As shown in Figure 39, click on Advanced Settings. <br />Figure 39<br />On the resulting page, add the URL of our Happy_Birthday_Card.docx file as shown in Figure 40. Remember we just pasted the document in the forms directory. Click OK. <br />Figure 40<br />Now, let’s check if our blank Birthday Card loads. As shown in Figure 41, click on New > Birthday Card<br />Figure 41<br />Our Birthday Card Word document should open up as shown in Figure 42. If it does, all is well and we can move on to the next step. Close the Word document without saving it. <br />Figure 42<br />This completes step 3.<br />QUESTION: Should our Happy Birthday Card been installed as part of the deployment process where a deployment package would be executed on each server in the farm?<br />ANSWER: Not necessarily. Remember that content types are stored in the content data base. It will therefore be available to all servers in the farm. So, installing it as we did here only need be done once. <br />STEP 4 – CREATE OUR INFOPATH 2007 FORM TO ACCEPT INPUT FROM THE USERS.<br />The purpose of this InfoPath form is to collect the name and birthday greeting from the person assigned the task of signing the card in the workflow. Also, we want it to be presented in a web page rather than in the InfoPath client (which is the default). We will have to specifically configure the InfoPath form to open in a web browser. We also will provide some validation. If either of the 2 fields are blank, we need to prevent the user from submitting the form. Also, we will need to provide the means to disable the Submit button if the workflow has been completed. <br />4.1 Open the InfoPath application and create a new blank form project<br />We now need to open the InfoPath application and create a new blank template as shown in Figure 43. <br />Figure 43<br />4.2 – Add a basic layout to the form<br />On the resulting screen under Design Tasks, click on Layout as shown in Figure44. <br />Figure 44<br />Select the Table with Title layout. You should then see a new layout as shown in Figure 45.<br />Figure 45<br />Change the title to Enter Birthday Card Message as shown in Figure 46.<br />Figure 46<br />This author changed the color scheme by clicking the navigation items as shown in Figure 47.<br />Figure 47<br />4.3 – Add 3 rows and split them into 2 columns<br />Next, we add 2 rows beneath the title, and then we split those rows into 2 columns as shown in Figure 31. This is done by right clicking on the table, and selecting the appropriate menu items to add rows and split into 2 columns. Refer to Figures 48 & 49.<br />Figure 48<br />Figure 49<br />4.4 Add a text box control to each row<br />From the right hand Task Pane as shown in Figure 50, click the drop down arrow and select Controls from the menu. We are now going to add a text box on each of our three rows in the table. <br />Figure 50<br />Drag a text box onto each row of our table as shown in Figure 51, and add the text in the left hand columns as shown. <br />Figure 51<br />4.5 – Modify the names of the default DataSource fields assigned to our text boxes<br />NOTE: When you create a form template, Microsoft Office InfoPath automatically creates a default data source for you. All InfoPath form templates contain a single, default data source.  You add fields and groups to the main data source by using the Data Source task pane or by dragging a control from the Controls task pane onto a view in the form template. When you drag a control onto a view, InfoPath adds fields and groups to the default data source according to the type of control you are adding. For example, if you drag a text box control onto your form template, InfoPath adds a field to the main data source. In our case, we see three fields added to the default data source since we dragged three text boxes onto our form. <br />Figure 52 – Our default data source showing our three fields<br />We now want to rename our fields to have them correspond to what our text boxes are intended to contain. Right click on the first text box, and select Text Box Properties as shown in Figure 53.<br />Figure 53<br />Change the name of the field to From, check the checkbox Cannot be blank and click OK. See Figure 54. Do the same for the 2nd text box, but enter the field name Message and click OK. Do the same for the 3rd text box but enter the name Completed, but do not check the checkbox Cannot be blank. Our form should look like Figure 55.<br />Figure 54<br />Also, from the Display tab, check the checkbox Read Only for the Completed text box. We do not want the user to be able to type in the Completed text box. We only want him/her to type in the From and Message text boxes.<br />Figure 55<br />4.6 - Add a button to our form<br />We will need a button for the user to click after he/she has filled out the form. We add that now. From the Task Pane on the right, click to get the drop down, and then select Controls from the resulting menu. Drag a button onto our form as shown in Figure 56.<br />Figure 56<br />4.7 Preview our form to see how it looks.<br />Click on the Preview button on the menu bar. The preview should appear as in Figure 57.<br />Figure 57 – Preview showing our red asterisks denoting cannot be blank<br />4.8 – Change the button title to Submit<br />Right click on the button, and click Button Properties from the menu. Enter the name Submit as shown in Figure 58. Click OK.<br />Figure 58<br />4.9 – Add a rule to the button to disable it if the text boxes are blank<br />As in typical .NET programming, we need some validation when the button is clicked on the form. We need to make sure the user entered something in the text boxes. How about if we disable the button until the text boxes contain something? Right click on the button and click on Conditional Formatting as in Figure 59.<br />Figure 59<br />On the resulting dialog box, Click Add. This causes a 2nd dialog to appear as in Figure 60. Add the condition as shown in the Figure. Also, note that the checkbox Disable this control is checked. This means that if either text box is blank, our Submit button will be disabled.<br />Figure 60 – if the condition is true, disable the button<br />We now have to add a rule that says when the Completed text box has a value of “1” (denoting the condition true), then we must disable the button in that case as well. So, add a second rule as we did for the first, except this time set the Conditional Format to that shown in Figure 61.<br />Figure 61<br />We can now test the validation by using the Preview button on the menu bar. In preview mode, add some text to both text boxes. The button should go from the disabled state to the enabled state as shown in Figure 61.<br />Figure 61 – test our form in preview mode<br />4.10 – Add a rule to send data to the server when the button is clicked.<br />We now need to configure the button so that it sends data to the server when the button is clicked. To do that, right click on the button as we have done before, and we get the dialog box as shown in Figure 62.<br />Figure 62<br />After you click the Rules button as shown in Figure 62, a new dialog will appear. On that dialog click Add to add a new rule. The resulting dialog will appear as in Figure 63.<br />Figure 63<br />Click on the Set Condition button and a new dialog will appear. Here we are going to configure the button to send data to the server when the From and Message text boxes are populated and the button is clicked. Refer to Figure 64.<br />Figure 64 – specify the condition<br />Click OK to get back to the previous dialog. On that dialog click on Add Action. In the resulting dialog, click on Submit using a Data Connection. Refer to Figure 65.<br />Figure 65 –specify the action for the condition<br />Now we have to configure the connection. Click the Add button and a new Wizard will appear as in Figure 66. <br />Figure 66 –we are going to submit the data to the server<br />Select the defaults as shown in Figure 66 and click Next. The wizard will move to the next screen as shown in Figure 67. <br />Figure 67<br />As shown in Figure 67, select To the hosting environment, such as an ASP.NET page or hosting application. Then click Next. The wizard will move to the next screen as shown in Figure 68.<br />Figure 68 – accept the default name<br />Accept the defaults as shown in the Figure and click Finish. When the wizard disappears, we need to add another action to the button. Bring up the dialog as we have done before as shown in figure 69.<br />Figure 69 – adding the action to close the form<br />Click on Add Action, and in the resulting dialog, click on Close the Form from the dropdown. The results should appear as in Figure 70.<br />Figure 70<br />Keep clicking OK to close out all the dialog boxes. So, what we have configured is that when the text boxes have text, enable the button. When the button is clicked, submit the data to the server and close the form.<br />4.11 – Configure the form to receive data from the workflow on load.<br />When the form is submitted, the signer of the card enters text into the From and Message fields that are sent to the server. Our C# code behind in the workflow will read the data submitted and will cause the task to be changed to the completed state when this happens. <br />However, the signer of the card could go back to the task list and try to submit the form again. When he/she clicks the Please sign this birthday card link on subsequent tries on the SharePoint Tasks page, the form will load again, but we want to populate the form on these subsequent loads. This is the case where we want to disable the Submit button on the form, and additionally, we would like to populate the form with the values that were originally submitted by the signer. We don’t want to simply put up a blank form again. Remember, the form was already successfully submitted and the task was placed in the completed state. <br />So, to populate the form on subsequent loads, and also disable the Submit button, we need to configure a secondary data connection. This data connection will be a receive data connection. <br />Adding the Receive Data Connection<br />NOTE: The creation of the ItemMetaData.xml file described in the following section was created by this author in the Workflow project in Visual Studio 2008, and the xml file is part of the solution files. It is recommended that you create a Visual Studio Workflow blank project, add an Xml file named ItemMetaData.xml and refer to that file in step 2 below. This tutorial will eventually get to create the workflow in VS2008. Refer to Figure 71. You don’t have to include the file in the project as this author has. You can simply save it to the directory of your choice if you want to.<br />Figure 71 – The authors VS2008 solution containing the ItemMetaData.xml file<br />We will want to update our task form from the workflow. To send data to the form fields from the workflow, we need to create a receive data connection. For this, we will use a very simple text (XML) file.<br /> As noted above, create an Xml file in Visual Studio 2008. Name it: ItemMetadata.xml. The name is case-sensitive and any other name will not work (Watch the case of the ‘d’ character, it’s lowercase).<br />Inside this file place the following line of text: <z:row xmlns:z=”#RowsetSchema” ows_From=”” ows_Message=”” ows_Completed=”” />.<br />Save and exit the file. If, in future you want to pass other data back to a task form, just create a similar file and reference the fields in your form by adding ows_ before the field name. You can have multiple fields, separated by a space.<br />Back in InfoPath, select Data Connections from the Tools menu.<br />Click the Add button and create a new data connection to receive data. A wizard will start as shown in Figure 72.<br />Select the radio button Receive Data as shown in Figure 72. Click Next.<br />Select XML document as our data source and click Next. See Figure 73.<br />In the next wizard screen, browse to ItemMetadata.xml file you just created and select it. Note that it’s the file in our Visual Studio 2008 project. See figure 74 and 75. Click Next.<br />IMPORTANT - As shown in Figure 76, select the radio button Include data as a resource file in the form or form template part. Click Next.<br />Leave the remaining settings at their default value and click Next until Finished.<br />Back in the form, double-click on the From field. <br />We’re going to set the default value to that passed into the data connection via the workflow. Click the Fx button next to the default value field as shown in Figure 78.<br />Click the Insert field or group button as shown in figure 79.<br />From the Data Source drop-down, select ItemMetadata as shown in Figure 80.<br />Select the ows_from field and click OK . Refer to Figure 80 again.<br />The field should appear as shown in Figure 81. Click OK until all dialogs close.<br />Repeat this process for the remaining two fields (E.g. Message and Completed)<br />Figure 72<br />Figure 73<br />Figure 74<br />Figure 75<br />Figure 76<br />Figure 77<br />Figure 78<br />Figure 79<br />Figure 80<br />Figure 81<br />Q. Where is the ItemMetaData.xml file after you reference it as a secondary data source? <br />A. It is embedded as a resource the InfoPath form. We specified this in Figure 76.<br />4.12 – Make the form browser compatible<br />We want our form to be displayed in a browser, not in the InfoPath client. That means that the form will appear in a web page in SharePoint. To do this, we have to make the form browser compatible.<br />On the top menu bar, click Tools > Form Options. A dialog box will appear as shown in Figure 82. On the left hand side, scroll down to Compatibility and click the check box Design a form template that can be opened in a browser or InfoPath. Then click OK. <br />Figure 82<br />4.13 – Configure the forms Security and Trust settings<br />The form will be opened in a web page, and SharePoint will require that the form be trusted. We need to configure the Form so that SharePoint will allow it to be opened.<br />On the top menu bar in InfoPath, click Tools > Form Options. A dialog box will appear as shown in Figure 83. On the left hand side, scroll down to Security and Trust and click the radio button Full Trust. Then, check the checkbox Sign this form certificate. You will need to click the button Create Certificate during the process. This certificate will only be a temporary certificate for our development purposes. In practice, a real certificate may be required. When finished, the dialog should look like Figure 83.<br />Note: This author found that SharePoint complained that the form was not trusted and would not allow it to be opened. Making the changes to Full Trust with certificate fixed the problem.<br />Figure 83<br />4.14 – Save the form<br />We can now save our form by clicking File > Save from the main menu. We are going to save our form to the same location that we saved our Word document. This author named the file HappyBirthdayForm_one.xsn.<br />Note: Do not end the name of the file in a number. Problems can arise since SharePoint can add numbers to the file name under certain conditions. <br />4.15 – Publish the form<br />We now need to publish our form. Click on File > Publish. The publishing wizard will appear. On the first Wizard dialog, we select the radio button To a Network Location. We then browse to the same location we stored our Word document. This author named this file HappyBirthdayForm_one_Published.xsn. Click out the wizard by taking <br />the defaults that are presented. <br />Note: The published .xsn file we create will be imported info Visual Studio later in this document. It will then be deployed as a feature where is will show up in Central Administration under Manage Form Templates automatically. <br />4.16 – Copy the ID of the form for use in Visual Studio. <br />We will need to enter the form ID in the workflow.xml file in Visual Studio since Visual Studio deployment will include our form as a feature. So, let’s copy the ID to the clipboard now. Click File > Properties as shown in Figure 84. <br />Figure 84<br />As shown in figure 85, copy the ID to the clipboard (and then to NotePad and save it). We will be pasting it into the workflow.xml file in Visual Studio when the time comes.<br />Figure 85 – We will need the ID of the form in Visual Studio<br /> Our form is now finished. We can now close the InfoPath application.<br />STEP 5 – CODE OUR WORKFLOW IN VISUAL STUDIO<br />We can now turn our attention to our workflow project in Visual Studio 2008. <br />5.1 Create the project<br />Click on File > New > Project as shown in Figure 86.<br />Figure 86<br />Select the SharePoint 2007 Sequential Workflow as shown in Figure 87.<br />Figure 87<br />As shown in Figure 88, configure the local site to where the workflow will be used. In this author’s case, it was his Team Site.<br />Figure 88 – The author chose his Team Site to use for debugging<br />As shown in Figure 89, select Happy Birthday Card Library from the dropdown list titled Library or List.<br />Figure 89<br />As shown in Figure 90, make sure the checkboxes appear as shown. We need to have the workflow started by either of the two means denoted by the check boxes. Click Finish and our workflow project is created. <br />Figure 90<br />5.2 – Add activity components from the Toolbox onto the design surface<br />We now need to drag some of the workflow components from the toolbox onto the design view. We are going to create a task with a content type, so we need to drag CreateTaskWithContent type object onto our design view. Drag the other objects onto the design view until we have a design view that looks like Figure 91.<br />Figure 91<br />Note: Each box on the form represents an activity. Blue activities are method activities and green activities are event handler activities. <br />5.3 – Add correlation tokens to our activities on the design view<br />We now need to set our correlation tokens for all the objects on the form.<br />Note on correlation tokens: Each blue and green box (a.k.a activity) on the design view must have a correlation token property set. The important point is that the correlation token for the OnWorkFlowActivated activity must NOT be the same as the other activities on the form (be they blue or green). So, when we set our correlation tokens, we will create a new correlation token name for all the activities OTHER than the OnWorkFlowActivated activity.<br />To set a correlation token for each activity, we right click on it, and select Properties from the drop down menu as shown in Figure 92. The procedure is the same for all the activities with the only difference being the name of the token we will enter. We will therefore have two tokens in play:<br /><ul><li>The OnWorkFlowCreated activity has a default correlation token name of workflowToken. We will accept this default.
  21. 21. We will create a new correlation token that will be used in every other activity. We will name that correlation token mainToken.</li></ul>Figure 92 – Right clicking on an activity to get the corresponding properties box<br />In the properties window for each of the activities (other than onWorkflowActivated1), we will type in the name mainToken. Figure 93 and 94 show the results. Note that we need to type (or cut and paste) in the name mainToken four times, once for each activity below onWorkflowActivated1.<br />Figure 93 – workflowToken is unique to onWorkflowActivated1<br />Figure 94 – All other activities use mainToken as the name<br />5.4 – Assign a task ID to createTaskWithContentType as a GUID (Globally Unique Identifier). <br />We now add a task ID property to the createTaskWithContentType activity. To get the GUID in Visual Studio, click Tools > Create Guid as shown in Figure 95.<br />Note: If your Visual Studio 2008 does not show the Create GUID menu item as shown in Figure 95, refer to Appendix B on how to get it to show up on the menu.<br />Figure 95<br />Click the radio button as shown in Figure 96 and click Copy. Then paste the GUID into the taskId property as shown in Figure 97.<br />Figure 96<br />Figure 97<br />5.5 – Assign the GUID from Figure 97 to the bottom 3 activities in the design view<br />The procedure we denote here will be repeated three times: one for each of the remaining activities (E.g. onTaskCreated1, onTaskchanged1 and completeTask1).<br />As shown in Figure 98, click on the ellipsis adjacent to the TaskId property for onTask1created. <br />Figure 98<br />On the resulting dialog under the tab Bind to an Existing member, under createTaskWithContentType1, click on TaskId. Refer to Figure 99.<br />Figure 99<br />Click OK, and the TaskId will now be associated with the taskId GUID from createTaskWithContentType1. Repeat this process for the remaining two activities.<br />5.6 – Create a rule for the whileActivity1<br />So, when the flow gets to the whileActivity1 activity box, what will cause the while loop to exit? At this point, we have to configure the activity to exit by means of a rule. The rule we will use is to exit when the value of a variable (which we will create in a moment) changes from false to true. The variable must be created in the C# code behind and we will name the variable _taskCompleted. Refer to Figure 100 which shows that we added C# code for the variable. <br />Figure 100 – we added a new variable called _taskCompleted (defaults to false)<br />Now we can set the rule for the while activity. As shown in the property pane for whileActivity1, click on the dropdown and click on Declarative Rule Condition.<br />Figure 101<br />Then, type in the name whileCondition1in the ConditionName field and then click on the resulting ellipsis as shown in Figure 102. <br />Figure 102 – Add the name, then click the elipses<br />In the resulting dialog box, type in !_taskCompleted for the condition as shown in Figure 103. Then click OK. <br />Figure 103<br />5.7 – Try a build: it should be successful<br />At this point, try building the solution. You should get a successful build with no errors.<br />5.8 – Add method stubs for the five activities in the design view<br />Now we need to add the method stubs that we will add code to in the code behind. For each of the activities, double click on each activity in design view. This will cause the stubs to be created for us behind the scenes as shown in Figure 104. <br />Figure 104 – an empty stub created for all five activities<br />5.9 – Add the code from Appendix C to the stubs.<br />The entire code for the workflow is shown in Appendix C. You can cut and paste the code into your solution. At this point, we will discuss the code blocks in turn.<br />5.9.1 - onWorkflowActivated1_Invoked<br /> private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e){<br /> workflowId = workflowProperties.WorkflowId;<br /> }<br />In the code created for us by Microsoft, we see that the workflowId is assigned a default GUID when the variable id declared. We really want that workflowID to be assigned to the same value as the workflowPropertiesId.<br />5.9.2 – createTaskWithContentType1_MethodInvoking<br />Figure 105<br />This function is called when the task with content type is created. Here we want to retrieve the Happy Birthday Card Word document from the workflowProperties list. It should be noted that there is only one item on the list since we have only 1 document in play at this time (E.g. our Happy Birthday Card document). Recall that the creator of the card typed in the Assigned To Login Name property in the Word document. We need to extract that name here and assign the name to a person who must perform the workflow task. We see that done on line 52. Once we have the assigned to name, we can create the SPWorkflowTaskProperties object required by the workflow. This is done on line 54. By specifying the person assigned to the task, the task to sign the birthday card will show up on that persons Task List in SharePoint.<br />5.9.3 – onTaskChanged1_Invoked<br />Figure 106<br />This function is called when the task changed event fires. For our workflow, this event is fired when the signer of the card submits the InfoPath form we created. This function contains the main code for the workflow. <br />Lines 92 and 93 fetch the From and Message strings from the InfoPath form. Note that we have to cast the ExternalDataEventArgs to SPTaskServiceEventArgs to get at these two fields. Next, on line 98, we try to find our Happy Birthday Card Word document as a SPListItem object. Recall that we need to populate two fields on the Happy Birthday Card Word document that were left blank by the creator of the card. The two fields are From and Message (which we just got from the InfoPath form on line 92 and 93). So, we assign those fields to the birthday card on lines 108 and 109. <br />Now, since the task is completed by the signer, we also set the Completed field in the Word document as well on line 110. Note that this has nothing to do with the Completed text box in the InfoPath form. The explanation of how the Completed text box works to disable the Submit button in the InfoPath form is given in Appendix D. <br />Note that we set the _taskCompleted variable to true on line 111. Recall we set the while loop condition so that the while loop exits when this value becomes true. This is a critical line of code. <br />5.9.4 – completeTask1_MethodInvoking<br />Figure 107<br />This function is called when the while loop exits. All we want to do here is to start the process of emailing our Happy Birthday Card to the intended recipient. So, we simply loop through the items in the library (there is only one in this example) and find our word document. Recall that the missing fields were populated in Figure 106. When we find the document, we pass it to our SendEmail() function. <br />5.9.5 – SendEmail<br />Figure 108<br />This function is used to send the Word Happy Birthday Card to the intended recipient. Note that our Word document is passed to this function as a SPListItem item, not a Word document object. Also, notice on line 197 that we obtain the reply to email address from SharePoint. We configured this in Central Administration > Operations > Outgoing email Settings as shown in Figure 109. Also, notice on line 199 that we specify the mail server to be that configured in SharePoint. In figure 109, that main server is the Outbound SMTP Server, smtp.live.com (which is the mail server for hotmail). The issue with using Hotmail as the mail server is that the hotmail mail server is locked down to prevent spam. So, to use it, you have to provide authentication. The authentication credentials are your hotmail username and password. To provide these credentials, we use the NetworkCredentials object (which is also used with many other things in .NET such as web service proxy authentication). Also note that we are specifying to use SSL on line 205. <br />Figure 109<br />5.9.6 – LogToEventLog<br />Figure 110<br />To provide logging to the Windows event log, we create our own log named HBEventLog. We navigate to the event log in Windows 2003 by clicking Start > Administrative Tools > Event Viewer. Our log is shown in Figure 111. <br />Figure 111<br />This concludes the discussion of the C# code. <br />STEP 6 – CONFIGURE WORKFLOW.XML AND FEATURE.XML IN VISUAL STUDIO<br />So, what is the mechanism by which our InfoPath form is deployed, and what is the mechanism which causes it to be displayed in a SharePoint web page when the signer of the birthday card clicks on the task Please sign this birthday card? (See Figure 6). The answer is in the files workflow.xml and feature.xml. Refer to Figure 112 which shows our Visual Studio project.<br />Figure 112<br />There are 3 main steps required for the InfoPath form configuration. They are:<br />Include the InfoPath form in the Visual Studio Project<br />Configure the file workflow.xml<br />Configure the file feature.cml<br />6.1 – Include the InfoPath form in the Visual Studio Project<br />The first thing to notice is that we have included our InfoPath form .xsn file in the project. The name is HappyBirthdayForm_one_Published_deployed.xsn. This form will be deployed as a feature when we click on the Build > Deploy HBWorkflow in Visual Studio. However, we will need to perform steps 2 and 3 for this to happen. The important thing here is that we include the .xsn file in the project. Recall from 4.15 that we published our .xsn file to a directory on disk. Simply include that file in the Visual Studio project (right click HBWorkflow > Add > Existing Item).<br />6.2 - Configure the file workflow.xml<br />Appendix E has the code for the workflow.xml file as does Figure 113. <br />Figure 113<br />There are 2 lines of interest here. They are lines 11 and 15. <br />Line 11 - We added line 11 with the hexadecimal number as shown. TaskListContentTypeId needs to be set to the value for your task type. If you do not use a custom task list defined in a content type, you can use the value 0x01080100C9C9515DE4E24001905074F980F93160 as shown. In our case, we do not use a custom task list so this number will be used as shown. <br />Line 15 – Remember when we copied our InfoPath form ID to the clipboard in Figure 85? That is the ID code we put on this line. The other thing to note is the name of the xml tag we place it in. The tag is <Task0_FormURN> and this tag denotes what form should be displayed for the first Task in our sequential work flow (note the character ‘0’). In our case, the first task is defined in the blue box createTaskWithContentType1 in design view. In our case we only have one task, so the Xml tag will be <Task0_FormURN> . What if we had a 2nd task object in design view and wanted a different InfoPath form to be displayed for that task? Well, we would define a new Xml tag <Task1_FormURN>. Notice that we incremented the number by 1. So, for every task in design view for which we need to show an InfoPath form, we add an XML tag but increment the number by 1. This is actually explained in the notes in the default Workflow.xml, but we have removed those notes for brevity.<br />6.3 - Configure the file feature.xml<br />The feature.xml file code is in Appendix F and in Figure 114. The key line here is line 13. <br />Line 13 specifies the .xsn file that should be deployed as a feature. When we deploy the project using the Visual Studio deployment menu item, the InfoPath form will be installed as a feature for us. Additionally, it will show up in Central Adminstration > Application Management > ManageFormTemplates as shown in Figure 115. We can verify this by going to c:program filescommon filesmicrosoft sharedweb server extensions12TEMPLATEFEATURESHBWorkflow and seeing the .xsn file there after the deployment takes place. See Figure 116. <br />Figure 114<br />Figure 115<br />Figure 116<br />CONCLUSION<br />We now have all the pieces in place. You can debug the program, or simply run it. Visual Studio will deploy the project each time you debug, so you don’t have to perform a 2 step process of build and deploy. Also, you should be able to exercise the program as denoted in the Figures at the beginning of this tutorial.<br />Hopefully, students of this tutorial will find this tutorial helpful in understanding some of the things we went over in class. Also, feedback is encouraged. <br />END DOCUMENT<br />APPENDICES FOLLOW<br />APPENDIX A<br />Figure <br />Note: On the resulting page shown in Figure, the Content Types section is shown. If it does not show on your screen, you need to click Advanced Settings on the page and in the resulting page click on the Yes radio button for Allow management of content types as shown in Figure <br />Figure <br />APPENDIX B<br />Display Create GUID on Visual Studio 2008 Tools menu.<br />1. Choose the Tools -> External Tools...<br />2. Click the Browse (…) button and find the guidgen.exe in the C:Program FilesMicrosoft SDKsWindowsv6.0ABin folder.<br />3. Click the OK button.<br />APPENDIX C<br />#region using<br />using System;<br />using System.Diagnostics;<br />using System.Net;<br />using System.Net.Mail;<br />using System.Workflow.Activities;<br />using Microsoft.SharePoint;<br />using Microsoft.SharePoint.Workflow;<br />using System.Reflection;<br />#endregion<br />namespace HBWorkFlow<br />{<br /> public sealed partial class Workflow1 : SequentialWorkflowActivity<br /> {<br /> #region Declarations<br /> private const string _formatedErrorMsg = " {0} : {1}" ;<br /> private const string _errorTitle = " Happy Birthday Workflow : " ;<br /> private bool _taskCompleted;<br /> public Guid workflowId = default(Guid);<br /> public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();<br /> #endregion<br /> #region Constructor<br /> /// <summary><br /> /// Default Constructor<br /> /// </summary><br /> public Workflow1()<br /> {<br /> InitializeComponent();<br /> }<br /> #endregion<br /> #region createTaskWithContentType1_MethodInvoking<br /> /// <summary><br /> /// Event handler called when createTaskWithContentType<br /> /// event is triggered.<br /> /// </summary><br /> private void createTaskWithContentType1_MethodInvoking(object sender, EventArgs e)<br /> {<br /> SPListItem birthdayCardWordDocument = null;<br /> try<br /> {<br /> SPList list = workflowProperties.List;<br /> foreach (SPListItem item in list.Items)<br /> if (item.Name.ToUpper().Equals(" HAPPY BIRTHDAY.DOCX" ))<br /> { //Address hard coding this later on<br /> birthdayCardWordDocument = item;<br /> break;<br /> }<br /> if (birthdayCardWordDocument == null)<br /> throw new SPException(" Could not find Word document named 'Happy Birthday.docx' on the list in the library. Unable to proceed." );<br /> string assignedTo = birthdayCardWordDocument.Properties[" Assigned To Login Name" ] as string;<br /> createTaskWithContentType1.TaskProperties = new SPWorkflowTaskProperties<br /> {<br /> AssignedTo = assignedTo,<br /> Title = " Please sign this birthday card!" ,<br /> Description = " InfoPath form presented to a signer of a Birthday Card" ,<br /> DueDate = DateTime.Now.AddDays(7)<br /> };<br /> }<br /> catch (Exception ex){<br /> LogToEventLog(_errorTitle,<br /> String.Format(_formatedErrorMsg, MethodBase.GetCurrentMethod(), ex.Message),<br /> EventLogEntryType.Error);<br /> }<br /> }<br /> #endregion<br /> #region onWorkflowActivated1_Invoked<br /> private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e){<br /> workflowId = workflowProperties.WorkflowId;<br /> <br /> }<br /> #endregion<br /> #region onTaskChanged1_Invoked<br /> /// <summary><br /> /// Event handler called when task is changed. This will occur<br /> /// when the user submits our InfoPath form.<br /> /// </summary><br /> private void onTaskChanged1_Invoked(object sender, ExternalDataEventArgs e){<br /> try{<br /> SPListItem birthdayCardWordDocument = null;<br /> string from = ((SPTaskServiceEventArgs)e).afterProperties.ExtendedProperties[" From" ] as string;<br /> string message = ((SPTaskServiceEventArgs)e).afterProperties.ExtendedProperties[" Message" ] as string;<br /> if (String.IsNullOrEmpty(from) || String.IsNullOrEmpty(message))<br /> throw new Exception(" From extended property was null and/or Message extended property was null." );<br /> SPList list = workflowProperties.List;<br /> foreach (SPListItem item in list.Items)<br /> if (item.Name.ToUpper().Equals(" HAPPY BIRTHDAY.DOCX" )){<br /> birthdayCardWordDocument = item;<br /> break;<br /> }<br /> if (birthdayCardWordDocument == null)<br /> throw new Exception(" Birthday Card Word document list item was null." );<br /> birthdayCardWordDocument.Properties[" From" ] = from;<br /> birthdayCardWordDocument.Properties[" Message" ] = message;<br /> birthdayCardWordDocument.Properties[" Completed" ] = " Completed" ;<br /> _taskCompleted = true;<br /> birthdayCardWordDocument.SystemUpdate();<br /> }<br /> catch (Exception ex){<br /> LogToEventLog(_errorTitle,<br /> String.Format(_formatedErrorMsg, MethodBase.GetCurrentMethod(), ex.Message),<br /> EventLogEntryType.Error);<br /> }<br /> }<br /> #endregion<br /> #region LogToEventLog<br /> /// <summary><br /> /// Our function to log to the Windows Event log<br /> /// </summary><br /> /// <param name=" errorTitle" >The title to be applied to the<br /> /// error entry.</param><br /> /// <param name=" message" >The message to appear in the event log.</param><br /> /// <param name=" msgType" >The message type (E.g. Error, information, etc.</param><br /> public static void LogToEventLog(string errorTitle, string message, EventLogEntryType msgType){<br /> EventLog hblog;<br /> try{<br /> if (!EventLog.SourceExists(" HBEventLogSource" ))<br /> EventLog.CreateEventSource(" HBEventLogSource" , " HBEventLog" );<br /> using (hblog = new EventLog { Source = " HBEventLogSource" })<br /> hblog.WriteEntry(errorTitle + " : " + message + " : " + msgType);<br /> }<br /> catch { }<br /> }<br /> #endregion<br /> #region completeTask1_MethodInvoking<br /> /// <summary><br /> /// Event handler called when the completeTask event is fired.<br /> /// </summary><br /> private void completeTask1_MethodInvoking(object sender, EventArgs e){<br /> SPListItem happyBirthdayWordDocument = null;<br /> try{<br /> SPList list = workflowProperties.List;<br /> foreach (SPListItem item in list.Items)<br /> if (item.Name.ToUpper().Equals(" HAPPY BIRTHDAY.DOCX" )){ //Address hard coding this later on.<br /> happyBirthdayWordDocument = item;<br /> break;<br /> }<br /> SendEmail(happyBirthdayWordDocument);<br /> }<br /> catch (Exception ex){<br /> LogToEventLog(_errorTitle,<br /> String.Format(_formatedErrorMsg, MethodBase.GetCurrentMethod(), ex.Message),<br /> EventLogEntryType.Error);<br /> }<br /> }<br /> #endregion<br /> #region SendEmail<br /> /// <summary><br /> /// Function used to send the Happy Birthday Card Word document<br /> /// as an attachment. <br /> /// </summary><br /> /// <param name=" happyBirthdayWordDocument" >Our Birthday Card as a SPListItem.</param><br /> private void SendEmail(SPListItem happyBirthdayWordDocument)<br /> {<br /> #region Programmers Notes<br /> //This author has SharePoint configured to use Microsoft Hotmail<br /> //as the outgoing mail server (E.g. smtp.live.com as the hotmail outgoing mail server).<br /> //This is set in Central Administration/Operations.<br /> //This mail server has security in place such as SSL and is currently requiring authorization.<br /> //We supply the authorization by means of a NetworkCredentials object. You will have to have an<br /> //account with the mail service you use so that you can supply your own credentials.<br /> #endregion<br /> try<br /> {<br /> string toAddress = happyBirthdayWordDocument.Properties[" Email address" ] as string;<br /> const string subject = " A Birthday Card For You!" ;<br /> const string messageText = " Please open the attached Birthday Card!" ;<br /> SPWeb web = workflowProperties.Web;<br /> string replyTo = web.Site.WebApplication.OutboundMailReplyToAddress;<br /> MailMessage mailMessage = new MailMessage(replyTo, toAddress, subject, messageText) { IsBodyHtml = false };<br /> string smtpAddress = web.Site.WebApplication.OutboundMailServiceInstance.Server.Address;<br /> SPFile attachmentFile = happyBirthdayWordDocument.File;<br /> Attachment attachment = new Attachment(happyBirthdayWordDocument.File.OpenBinaryStream(), attachmentFile.Name);<br /> mailMessage.Attachments.Add(attachment);<br /> SmtpClient smtp = new SmtpClient(smtpAddress)<br /> {<br /> EnableSsl = true,<br /> UseDefaultCredentials = false,<br />Credentials = new NetworkCredential(" yourname@hotmail.com" , " yourpassword" )<br /> };<br /> smtp.Send(mailMessage);<br /> }<br /> catch (Exception ex)<br /> {<br /> LogToEventLog(_errorTitle,<br /> String.Format(_formatedErrorMsg, MethodBase.GetCurrentMethod(), ex.Message),<br /> EventLogEntryType.Error);<br /> }<br /> }<br /> #endregion<br /> <br /> }<br />}<br />APPENDIX D – InfoPath form feedback<br />This appendix will attempt to describe the mechanism by which the submit button on the InfoPath 2007 form is disabled after the workflow completes. <br />Recall that in Figure 61, we specified that the button should be disabled whenever the text in the Completed text box is set to the number “1”. Nowhere in our code do we see that we as programmers have set this value. In fact, the default value of “0” in the text box when the form is loaded does not come from our C# code either.<br />In the topic 4.11 – Configure the form to receive data from the workflow on load, we specified a secondary data source to receive data from the host whenever the InfoPath form loads. We also specified that the ItemMetaData.xml file be used as the secondary data source on receive. If we exercise the sending of the Happy Birthday Card workflow, we see that the InfoPath form contains a value of “0” on first load. However, when the form is submitted, invoking the form a 2nd time shows the value has been set to “1”. Again, we as programmers had nothing to do with these values appearing in the text box.<br />This happens because we use the name ows_Completed in the Xml file and reference it in the properties of the Completed text box on the form as shown in Figure A1. This tag appears to be linked to a Boolean value denoting whether or not the task is in the completed state. This goes on behind the scenes and appears to be an undocumented feature from Microsoft. <br />Figure A1<br />If we tried to substitute a different ows_ value, such as ows_Done (or other name of your choice), then the mechanism will not work, mainly because we cannot set the value to “1” in the C# code behind after the task completes. The key point here is that we would need to set the value after the task completes, but that is too late in the flow to change it.<br />As an alternative, if we were to attempt to set a value in the Completed text box before the task completes, say in the createTaskWithContentTpe1_MethodInvoking event handler, then it would work. The value “1” would appear in the InfoPath form. However, the user would be helpless since he/she could not submit the form since the submit button would be disabled on first load! The form is dead from the outset. Not good. We want the “1” to appear on the subsequent loads, but again, that is after the task is completed.<br />To set the value of the form on first load (which is useless for us as mentioned above) would require that we include a new property in our C# code called createTaskWithContentType1_TaskProperties since that would give us access to the extendedProperties object. Our code would look like Figures A2 and A3. <br />So, where does this leave us to disable the submit button after the task completes? Well, we must use the undocumented feature where the Boolean value of “0” and “1” is provided for us by using ows_Completed as the name in the ItemMetaData.xml file and specified in the binding for the Completed form.<br />It would be great if readers of this tutorial could research this further and find documentation on this phenomenon. <br />Figure A2<br />Figure A3 – This line would set the value in the InfoPath form on 1st load. <br />APPENDIX E – workflow.xml<br /><?xml version=" 1.0" encoding=" utf-8" ?><br /><Elements xmlns=" http://schemas.microsoft.com/sharepoint/" ><br /><Workflow<br /> Name=" HBWorkFlow" <br /> Description=" My SharePoint Workflow" <br /> Id=" 68cfd787-087f-4baf-8718-00666bd6afd4" <br /> CodeBesideClass=" HBWorkFlow.Workflow1" <br /> CodeBesideAssembly=" HBWorkFlow, Version=, Culture=neutral, PublicKeyToken=c588e451cdda9911" <br /> TaskListContentTypeId=" 0x01080100C9C9515DE4E24001905074F980F93160" ><br /><Categories/><br /><MetaData><br /><Task0_FormURN>urn:schemas-microsoft-com:office:infopath:HappyBirthdayForm-one-Published-deployed:-myXSD-2009-09-15T14-10-01</Task0_FormURN><br /><StatusPageUrl>_layouts/WrkStat.aspx</StatusPageUrl><br /></MetaData><br /></Workflow><br /></Elements><br />APPENDIX F – Feature.xml<br /><?xml version=" 1.0" encoding=" utf-8" ?><br /><Feature Id=" 648c9208-3138-4bf3-acd4-06b7e5e51b11" <br /> Title=" HBWorkFlow feature" <br /> Description=" My SharePoint Workflow Feature" <br /> Version="" <br /> Scope=" Site" <br /> ReceiverAssembly=" Microsoft.Office.Workflow.Feature, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" <br /> ReceiverClass=" Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver" <br /> xmlns=" http://schemas.microsoft.com/sharepoint/" ><br /> <ElementManifests><br /> <ElementManifest Location=" workflow.xml" /><br /><ElementFile Location=" HappyBirthdayForm_one_Published_deployed.xsn" /><br /> </ElementManifests><br /> <Properties><br /> <Property Key=" GloballyAvailable" Value=" true" /><br /> <Property Key=" RegisterForms" Value=" *.xsn" /><br /> </Properties><br /></Feature><br />