<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-55538164283100985</id><updated>2012-02-16T09:22:43.729-05:00</updated><category term='web-enabled form'/><category term='PickerEntity'/><category term='consumer'/><category term='list library'/><category term='web part'/><category term='provider'/><category term='list library document library workflows'/><category term='connected web parts'/><category term='solution'/><category term='task emails'/><category term='workflow'/><category term='tutorial'/><category term='web part custom property'/><category term='deployment'/><category term='contact list'/><category term='update 12-hive'/><category term='workflow task emails'/><category term='WSPBuilder'/><category term='list template'/><category term='form library'/><category term='requiredfieldvalidator'/><category term='entity'/><category term='interface'/><category term='PeopleEditor'/><category term='web part gallery'/><category term='regularexpressionvalidator'/><category term='sharepoint'/><category term='SharePoint MOSS 2007 workflow deploy wsp'/><category term='SharePoint group'/><category term='feature'/><category term='infopath'/><category term='state machine workflow'/><category term='managed code'/><category term='malfunctioning'/><category term='task form'/><category term='initiation form'/><category term='infopath form'/><category term='error'/><title type='text'>Simple SharePoint Solutions</title><subtitle type='html'>This blog is dedicated to providing simple, easy to follow solutions to SharePoint development challenges.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8074636612447891136</id><published>2011-11-19T07:37:00.005-05:00</published><updated>2011-11-19T07:59:19.432-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SharePoint MOSS 2007 workflow deploy wsp'/><title type='text'>The easiest way to package and deploy a Sharepoint workflow</title><content type='html'>Note: this relates to packaging up MOSS 2007 workflows created in Visual Studio. &lt;br /&gt;&lt;br /&gt;If you have developed your workflow in Visual Studio and have already been F5 deploying it (which means you have already created your feature.xml and workflow.xml), and now you are ready to package it up into a wsp for deploying to other servers, then the following is probably the quickest way of doing that.&lt;br /&gt;&lt;br /&gt;Essentially, it involves using WSP Builder. However, this method doesn't require using any of the WSP Builder project templates to do it. The templates are useful if you create your workflow project from the WSP Builder templates to begin with. But if you created your workflow with the normal Visual Studio workflow templates, THEN want to use WSP Builder templates for packaging the workflow, its a bit of a pain to do. This will enable you to get your .wsp with the absolute minimum in changes to your Visual Studio project.&lt;br /&gt;&lt;br /&gt;1. install WSP Builder, then load Visual Studio and your workflow project&lt;br /&gt;&lt;br /&gt;2. in your Project, create 12/TEMPLATE/FEATURES/your_feature_name&lt;br /&gt;&lt;br /&gt;3. copy feature.xml, workflow.xml, and your InfoPath task forms (.xsn's) into this folder&lt;br /&gt;&lt;br /&gt;4. right-click your project, click WSPBuilder -&gt; Build WSP&lt;br /&gt;&lt;br /&gt;WSP Builder will automatically&lt;br /&gt;1. build your manifest.xml for you, adding in everything it sees in your 12 folder structure&lt;br /&gt;2. get your project's assembly (.dll)&lt;br /&gt;3. package it all into a .wsp file and drop it in the root of your project&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Below are my installation and removal scripts.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;&lt;pre&gt;rem Workflow installation script&lt;br /&gt;&lt;br /&gt;d:\stsadm -o addsolution -filename d:\deploy\MyWorkflowProject\MyWorkflowProject.wsp&lt;br /&gt;&lt;br /&gt;d:\stsadm -o deploysolution -name MyWorkflowProject.wsp -immediate -allowGacDeployment&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;d:\stsadm -o activatefeature -name MyWorkflowProject -url http://server_to_deploy_to&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt; &lt;br /&gt; &lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;&lt;pre&gt;rem Workflow removal script&lt;br /&gt;&lt;br /&gt;d:\stsadm -o deactivatefeature -name MyWorkflowProject -url http://server_to_deploy_to&lt;br /&gt;&lt;br /&gt;d:\stsadm -o retractsolution -name MyWorkflowProject.wsp -immediate&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;d:\stsadm -o deletesolution -name MyWorkflowProject.wsp -override&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8074636612447891136?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8074636612447891136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/11/easiest-way-to-package-and-deploy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8074636612447891136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8074636612447891136'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/11/easiest-way-to-package-and-deploy.html' title='The easiest way to package and deploy a Sharepoint workflow'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8136859705903015969</id><published>2011-10-18T21:53:00.004-04:00</published><updated>2011-10-19T00:32:26.746-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SharePoint group'/><category scheme='http://www.blogger.com/atom/ns#' term='task emails'/><title type='text'>How to email members of a SharePoint group</title><content type='html'>This relates to MOSS 2007.&lt;br /&gt;&lt;br /&gt;In doing a state machine workflow, I have noticed how you can assign a SharePoint group to a task no problem, but when you set that group as the recipient of an email activity, you get an error. I wrote a very simple function that takes in the SharePoint group's name as a parameter and returns a semi-colon delimited string of the email addresses of the people in the group. I also had another scenario where I wanted to get the login names of the individuals in the group, too, so I made another parameter that acts as a switch - you choose whether you want login names or email addresses. Just assign the function below to your email task's To property and you are good to go!&lt;br /&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;&lt;pre&gt;Private Function GetGroupInfo(ByVal GroupName As String, ByVal WhatToGet As String) As String&lt;br /&gt;    'PURPOSE: returns semicolon-separated string of group member login names or email addresses&lt;br /&gt;&lt;br /&gt;    Dim strReturnString As String = ""&lt;br /&gt;&lt;br /&gt;    'get group&lt;br /&gt;    Dim TheGroup As SPGroup = workflowProperties.Web.SiteGroups(GroupName)&lt;br /&gt;&lt;br /&gt;    'loop through group and build string of login names or email addresses&lt;br /&gt;    For Each GroupMember As SPUser In TheGroup.Users&lt;br /&gt;&lt;br /&gt;        If WhatToGet = "LOGIN_NAMES" Then&lt;br /&gt;&lt;br /&gt;            If strReturnString = "" Then&lt;br /&gt;                strReturnString = GroupMember.LoginName&lt;br /&gt;            Else&lt;br /&gt;                strReturnString = strReturnString &amp; "; " &amp; GroupMember.LoginName&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        ElseIf WhatToGet = "EMAIL_ADDRESSES" Then&lt;br /&gt;&lt;br /&gt;            If strReturnString = "" Then&lt;br /&gt;                strReturnString = GroupMember.Email&lt;br /&gt;            Else&lt;br /&gt;                strReturnString = strReturnString &amp; "; " &amp; GroupMember.Email&lt;br /&gt;            End If&lt;br /&gt;        End If&lt;br /&gt;    Next&lt;br /&gt;&lt;br /&gt;    Return strReturnString&lt;br /&gt;&lt;br /&gt;End Function&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;To get the email addresses of members in a SharePoint group called "Account Personnel" and then assign them to an email activity's To property, type:&lt;br /&gt;MyTaskEmail_To = GetGroupInfo("Account Personnel", "EMAIL_ADDRESSES")&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8136859705903015969?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8136859705903015969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/10/how-to-email-members-of-sharepoint.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8136859705903015969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8136859705903015969'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/10/how-to-email-members-of-sharepoint.html' title='How to email members of a SharePoint group'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-6762565238601496851</id><published>2011-10-18T19:24:00.005-04:00</published><updated>2011-10-18T21:51:49.385-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='workflow task emails'/><title type='text'>Workflow task link in email</title><content type='html'>Just wanted to do a quick post on a recent gotcha. I created a MOSS 2007 state machine workflow and added custom email activities to send emails notifying users of their tasks. I wanted to have a link in those emails that pointed directly to the user's task, ie the same link you get when you enable "Send e-mail when ownership assigned?" in the task list's Settings -&gt; List Settings -&gt; Advanced settings screen.&lt;br /&gt;&lt;br /&gt;  I thought I could simply reference the task's ListItemId property, but it kept coming up as -1. After finding an article on &lt;a href="http://sharepointricks.blogspot.com/2009/05/1-private-int-getnextavailableidfromlis.html"&gt;SharePoint Tricks&lt;/a&gt;, I realized I needed to create a Field variable for the ListItemId property, then reference the Field variable.&lt;br /&gt;&lt;br /&gt;1. In the create task activity, create a Field from the ListItemId (by default it says -1). &lt;br /&gt;&lt;br /&gt;2. The link in the email concatenates workflowProperties.SiteUrl, workflowProperties.TaskListUrl,  "/DispForm.aspx?ID=", and finally createMyApprovalTask_ListItemId.ToString()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This will link directly to the user's task form on the SharePoint site.&lt;br /&gt;&lt;br /&gt;That's it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-6762565238601496851?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/6762565238601496851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/10/workflow-task-link-in-email.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6762565238601496851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6762565238601496851'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/10/workflow-task-link-in-email.html' title='Workflow task link in email'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-3023678257013603429</id><published>2011-09-28T23:31:00.001-04:00</published><updated>2011-09-28T23:31:00.393-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='error'/><category scheme='http://www.blogger.com/atom/ns#' term='malfunctioning'/><title type='text'>How to remove a SharePoint solution that is giving an error</title><content type='html'>A while back I had a solution that would just not be removed. In the Solution Management screen in Central Administration, the Status for the solution was a bright red "Error". Attempting to remove it from there would give an error.&lt;br /&gt;&lt;br /&gt;Luckily, I found a great post by Alex Thissen that clearly explains how to use stsadm to remove a malfunctioning solution. The blog post is called &lt;a href="http://www.alexthissen.nl/blogs/main/archive/2007/07/18/removing-malfunctioning-windows-sharepoint-services-solutions.aspx"&gt;Remove malfunctioning Windows SharePoint Services solutions&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In a nutshell, if retracting the solution:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;stsadm -o retractsolution -name solutionname.wsp -immediate&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;doesn't work, and if deleting the solution:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;stsadm -o deletesolution -name solutionname.wsp -override&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;doesn't work, then&lt;br /&gt;&lt;br /&gt;1. run an enumeration to see the scheduled deployments&lt;br /&gt;&lt;br /&gt;stsadm -o enumdeployments&lt;br /&gt;&lt;br /&gt;2. use the JobId to cancel the deployment&lt;br /&gt;&lt;br /&gt;stsadm -o canceldeployment -id 2529c788-971c-46a3-b69f-a2a0a1fcc851&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Afterwards, you can verify it by doing 1. again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-3023678257013603429?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/3023678257013603429/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/09/how-to-remove-sharepoint-solution-that.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/3023678257013603429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/3023678257013603429'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/09/how-to-remove-sharepoint-solution-that.html' title='How to remove a SharePoint solution that is giving an error'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8200308305774996124</id><published>2011-09-22T21:33:00.002-04:00</published><updated>2011-09-22T21:33:00.161-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to start a SharePoint State Machine workflow without association or initiation forms</title><content type='html'>This assumes you are creating a SharePoint 2007 state machine workflow in Visual Studio.&lt;br /&gt;&lt;br /&gt;If you ever need a workflow to start, but don't want it to have an association or initiation form, you have to do two things, both of which are in workflow.xml:&lt;br /&gt;&lt;br /&gt;1. In the Workflow element, do not specify an AssociationUrl (for the association form) or InstantiationUrl (for the initiation form). Just leave it out of the element completely! You may want to do the same thing if you have no modification form.&lt;br /&gt;The attributes indicate the web forms that will host the association and initiation forms. By default, they look like &lt;b&gt;AssociationUrl="_layouts/CstWrkflIP.aspx"&lt;/b&gt; and &lt;b&gt;InstantiationUrl="_layouts/IniWrkflIP.aspx"&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. In the MetaData element, be sure Association_FormURN and Instantiation_FormURN are commented out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8200308305774996124?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8200308305774996124/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/09/how-to-start-sharepoint-state-machine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8200308305774996124'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8200308305774996124'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/09/how-to-start-sharepoint-state-machine.html' title='How to start a SharePoint State Machine workflow without association or initiation forms'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-6863496413815354974</id><published>2011-06-26T23:27:00.000-04:00</published><updated>2011-06-27T00:42:43.646-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='form library'/><category scheme='http://www.blogger.com/atom/ns#' term='managed code'/><category scheme='http://www.blogger.com/atom/ns#' term='infopath form'/><category scheme='http://www.blogger.com/atom/ns#' term='web-enabled form'/><title type='text'>How to create and deploy a web-enabled InfoPath form with managed code</title><content type='html'>In this article, we will create a web-enabled InfoPath form that will have managed code in its "on load" that will populate a field automatically. We will deploy our InfoPath form to a SharePoint form library.&lt;br /&gt;&lt;br /&gt;First off: this article assumes you are using MOSS 2007, Microsoft Office InfoPath 2007, and Visual Studio 2008. To add managed code to an InfoPath form,  Visual Studio Tools for Applications (VSTA) must be installed.&lt;br /&gt;&lt;br /&gt;If VSTA is not set up, the following message appears:&lt;br /&gt;&lt;br /&gt;"InfoPath cannot add the event handler.&lt;br /&gt;&lt;br /&gt;To work with Visual Basic or C# code, VSTA is required. .NET Framework 2.0 and Microsoft Core XML Services 6.0 must be installed before VSTA."&lt;br /&gt;&lt;br /&gt;To install VSTA:&lt;br /&gt;----------------&lt;br /&gt;1. Control Panel -&gt; Add or Remove Programs -&gt; Microsoft Office 2007 -&gt; Change&lt;br /&gt;&lt;br /&gt;2. Under Microsoft Office InfoPath -&gt; .NET Programmability Support, check the .NET Programmability Support for .NET Framework version 2.0 and then under that check Visual Studio Tools for Applications&lt;br /&gt;&lt;br /&gt;Also, be sure Microsoft Core XML Services 6.0 is installed (it will show up as MSXML6). Check for it in Add or Remove Programs, and if it is not installed, search for it on microsoft.com, then download it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, on to why we are here...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To create a web-enabled InfoPath form with managed code:&lt;br /&gt;--------------------------------------------------------&lt;br /&gt;1. Create the form in InfoPath. I won't go in detail here. Just create a form with a few fields. Call one of the fields AccountId.&lt;br /&gt;&lt;br /&gt;2. change compatibility settings to make it web-enabled:&lt;br /&gt;   Tools -&gt; Form Options -&gt; Compatibility -&gt; check Design a form template that can be opened in a browser or InfoPath&lt;br /&gt;&lt;br /&gt;3. give the form Full Trust:&lt;br /&gt;   Tools -&gt; Form Options -&gt; Security and Trust -&gt; select Full Trust&lt;br /&gt;&lt;br /&gt;4. add code - to have custom code when the form loads:&lt;br /&gt;   a. set location where you want the Visual Studio project to be created&lt;br /&gt;      Tools -&gt; Programming -&gt; under Programming Language, specify language and project location&lt;br /&gt;   b. indicate you want to code for the loading of the form&lt;br /&gt;      Tools -&gt; Programming -&gt; Loading Event... (it will prompt you to save the form&lt;br /&gt;        if you haven't already)&lt;br /&gt;      VSTA will create a Visual Studio project (using the form's name) in the location&lt;br /&gt;        specified in a.  Visual Studio will load with project opened.&lt;br /&gt;   c. add code to populate a form field automatically when form loads&lt;br /&gt;      under FormEvents_Loading, add code to execute when form loads&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;Dim strAccountId As String = ""&lt;br /&gt;&lt;br /&gt;            strAccountId = "P-" &amp; Date.Now.ToString()&lt;br /&gt;&lt;br /&gt;            'populate account field on infopath form with account #&lt;br /&gt;            Me.CreateNavigator.SelectSingleNode("my:MyForm/my:AccountId", Me.NamespaceManager).SetValue(strAccountId )&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;5. run the project to see the form field being populated&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To deploy a web-enabled InfoPath form with managed code:&lt;br /&gt;--------------------------------------------------------&lt;br /&gt;6. publish the form template&lt;br /&gt;   back in InfoPath, &lt;br /&gt;   a. click Publish Form Template...&lt;br /&gt;   b. select To a network location and click Next&lt;br /&gt;   c. enter a location to publish the template, a place where an administrator can access and click Next, clear textbox then click Next, click Publish, click Close&lt;br /&gt;&lt;br /&gt;7. upload Form Template in Central Admin&lt;br /&gt;   a. in Central Administration -&gt; Application Management -&gt; Manage Form Templates, click Upload Form Template&lt;br /&gt;   b. browse to location you just published to and select form template&lt;br /&gt;   c. click Upload&lt;br /&gt;&lt;br /&gt;8. activate the form at the site collection level&lt;br /&gt;   In Site Settings -&gt; Site Collection Features, find form template and click Activate&lt;br /&gt;   Notice the form template appears as a Site Content Type under Site Settings -&gt; Site Content Types&lt;br /&gt;&lt;br /&gt;9. create and set up a form library and add that content type to it&lt;br /&gt;   create new form library&lt;br /&gt;   a. I go in through View All Site Content -&gt; Create -&gt; Form Library&lt;br /&gt;   b. give it a Name&lt;br /&gt;   c. Document Template should have Microsoft Office InfoPath form&lt;br /&gt;   d. click Create&lt;br /&gt;&lt;br /&gt;10. set up library&lt;br /&gt;   a. settings -&gt; form library settings -&gt; advanced settings&lt;br /&gt;      check Yes for Allow management of content types&lt;br /&gt;      check Display as a web page&lt;br /&gt;      check No for Display "New Folder" command on the New menu (I just like to remove NewFolder, this isn't really necessary)&lt;br /&gt;      click Ok&lt;br /&gt;   b. Under Content Types, remove Form and add new content type (form template)&lt;br /&gt;      a. click Form -&gt; Delete this content type&lt;br /&gt;      b. click Add from existing site content types, select new content type, click Add, then click OK&lt;br /&gt;&lt;br /&gt;11. go back to form library and click New&lt;br /&gt;&lt;br /&gt;12. You should now see the form display as a web page, and notice the AccountId field was populated programmatically!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-6863496413815354974?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/6863496413815354974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/06/how-to-create-and-deploy-web-enabled.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6863496413815354974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6863496413815354974'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2011/06/how-to-create-and-deploy-web-enabled.html' title='How to create and deploy a web-enabled InfoPath form with managed code'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-1165373381787220083</id><published>2010-12-17T20:39:00.009-05:00</published><updated>2010-12-18T10:19:29.956-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='list library document library workflows'/><title type='text'>How to get a list of running workflows in a document library</title><content type='html'>Getting a list of workflows that are running on a list library or document library is pretty straightforward. Essentially, all we need to do is get a handle on the site, get a handle on the list, loop through the list's items, then for each item, loop through that item's workflows collection, then for each workflow, look at its InternalState and if its set to "Running", its, uhh, running. You could remove the IF statement from the code below and display all workflows for the current item to see whats Running, Completed, Locked, or Canceled. Once you have a handle on the workflow, you could then do whatever else you might need to do with it. What if you wanted to be emailed every time a workflow got locked? In my case, I have a document library with items that may have workflows running on them. As these workflows have due dates, I need to look at all running workflows in the list, and if today is past their due date, I want to stop these workflows. I will then put this code in a SharePoint timer job that will execute daily. In fact, I will post an article on this in the coming days or weeks after I actually do it! This is using VS2008/MOSS2007 and the code is in VB, but it would be extremely easy to rewrite in C#. I would imagine this would work in SharePoint 2010, but I haven't tested yet. If anyone wants to copy/paste it in 2010 and test it for the rest of us, we'd love a confirmation!&lt;br /&gt;&lt;br /&gt;For simplicity's sake, we will create a console application that will access our SharePoint site and loop through the document library's workflows to find the running ones.&lt;br /&gt;&lt;br /&gt;1. in Visual Studio, click File -&gt; New Project -&gt; Windows -&gt; Console Application&lt;br /&gt;&lt;br /&gt;2. it defaults to ConsoleApplication1, leave it and click OK&lt;br /&gt;&lt;br /&gt;3. in Solution Explorer, right-click ConsoleApplication1, click Add Reference&lt;br /&gt;&lt;br /&gt;4. browse to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI\ and select Microsoft.SharePoint.dll&lt;br /&gt;&lt;br /&gt;5. above Module Module1, add:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;&lt;pre&gt;Import Microsoft.SharePoint&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;6. inside Sub Main(), add:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;&lt;pre&gt;Dim site As New SPSite("http://my_sp_site_here")   'get site&lt;br /&gt;Dim list As SPList = site.RootWeb.Lists("my_document_library")   'get document library&lt;br /&gt;&lt;br /&gt;For Each item As SPListItem In list.Items   'for each item in library&lt;br /&gt;  For Each wrkflw As Workflow.SPWorkflow In item.Workflows    'for current item's list of workflows&lt;br /&gt;    If wrkflw.InternalState.ToString = "Running" Then   'if current workflow is running&lt;br /&gt;      Console.WriteLine(wrkflw.InstanceId.ToString)   'display it's instance id&lt;br /&gt;    End If&lt;br /&gt;  Next&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;Console.WriteLine()&lt;br /&gt;Console.Write("Press ENTER to continue")&lt;br /&gt;Console.ReadLine()&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-1165373381787220083?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/1165373381787220083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/12/how-to-get-list-of-running-workflows-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1165373381787220083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1165373381787220083'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/12/how-to-get-list-of-running-workflows-in.html' title='How to get a list of running workflows in a document library'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-5937309416855013178</id><published>2010-06-09T09:18:00.020-04:00</published><updated>2010-06-10T08:38:43.487-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='feature'/><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='update 12-hive'/><title type='text'>Update 12 Hive Through A Feature</title><content type='html'>As we got our SharePoint environment up and running, we needed to have the ability to make changes to our 12-Hive. We also wanted to have the flexibility to undo the changes and restore the 12-Hive to the way it was originally.&lt;br /&gt;&lt;br /&gt;This post discusses the scenario where you need to make changes to files that already exist in the 12-Hive. I call this solution &lt;b&gt;HiveUpdates&lt;/b&gt;. If you have a need to add new files to the 12-Hive that do not already exist, such as new images or layouts, check out my other article &lt;a href="http://simplesharepointsolutions.blogspot.com/2010/06/add-new-files-to-12-hive-through_2091.html"&gt;Add New Files To 12-Hive Through A Solution&lt;/a&gt;. I call that solution &lt;b&gt;HiveAdditions&lt;/b&gt;. We decided it would be best for us to separate these two types of Hive changes into two separate solutions. &lt;br /&gt;&lt;br /&gt;This solution, &lt;b&gt;HiveUpdates&lt;/b&gt;, allows us to modify existing Hive files. This solution contains a feature that, when activated, makes a backup copy of the original file, then overwrites the original with the new file. When the feature is deactivated, it copies the backup file back to the original filename.&lt;br /&gt;&lt;br /&gt;The method I used is based on the article &lt;a href="http://weblogs.asp.net/sharadkumar/archive/2009/06/21/sharepoint-2007-12-hive-system-file-changes-one-feature-to-rule-them-all.aspx"&gt;12 Hive System-File Changes: One Feature to rule them all!&lt;/a&gt;. The above post has a routine that is called when the feature is activated or when its deactivated. The code that fires simply "swaps" newly changed 12-hive files with their corresponding original 12-hive files. This happens both when the feature is activated or deactivated. While the "swap" code seems like a good idea on the surface (the same code that deploys the changes will swap files back to undeploy them), I found in practice this seemed risky and even prone to error if something went wrong on the feature activate or feature deactivate. I sometimes got the files out of sync and would lose the original 12-Hive versions.&lt;br /&gt;&lt;br /&gt;Therefore, I modified the code to first make a backup copy of a 12-Hive file I want to update, then overwrite the original 12-hive file with my changed version (this is an activate). On a deactivate, it overwrites my changed version with the original's backup file. The original's backup file always stays there as a backup, which makes me feel very safe. And I like feeling safe. I like my job and want to keep it.&lt;br /&gt;&lt;br /&gt;1. In Visual Studio 2008, File -&gt; New -&gt; Project&lt;br /&gt;&lt;br /&gt;2. Under Visual C#, pick SharePoint, then Empty&lt;br /&gt;&lt;br /&gt;3. For Name, type: HiveUpdates&lt;br /&gt;&lt;br /&gt;4. For Location, type: D:\development&lt;br /&gt;&lt;br /&gt;5. Click OK&lt;br /&gt;&lt;br /&gt;6. Select Full Trust (Deploy to GAC) and click OK&lt;br /&gt;&lt;br /&gt;7. In WSP View (View -&gt; Other Windows -&gt; WSP View), click "Create new feature"&lt;br /&gt;&lt;br /&gt;8. Change Feature Scope to Farm&lt;br /&gt;&lt;br /&gt;9. Check Add feature receiver&lt;br /&gt;&lt;br /&gt;10. click OK&lt;br /&gt;&lt;br /&gt;11. In Solution Explorer, rename Feature1 to HiveUpdates&lt;br /&gt;&lt;br /&gt;12. Rename Feature1Receiver.cs to HiveUpdatesReceiver.cs&lt;br /&gt;&lt;br /&gt;13. Double-click HiveUpdatesReceiver.cs to open it&lt;br /&gt;&lt;br /&gt;14. Rename class from FeatureReceiver1 to HiveUpdatesReceiver&lt;br /&gt;&lt;br /&gt;15. Below that, you'll see the constructor "public FeatureReceiver1". Rename constructor from FeatureReceiver1 to HiveUpdatesReceiver.&lt;br /&gt;&lt;br /&gt;16. In WSP View, hit refresh and notice FeatureReceiver1 is now HiveUpdatesReceiver&lt;br /&gt;&lt;br /&gt;17. Rename Feature1 folder to HiveUpdates&lt;br /&gt;&lt;br /&gt;Make the following code modifications/additions to HiveUpdatesReceiver.cs:&lt;br /&gt;&lt;br /&gt;18. At the top of the code, add the following using statements:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;using Microsoft.SharePoint.Administration;&lt;br /&gt;using System.IO;&lt;/div&gt;&lt;br /&gt;19. Above the HiveUpdatesReceiver constructor, add the following:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;private DirectoryInfo localDirectory = new DirectoryInfo(@"C:\");&lt;/div&gt;&lt;br /&gt;20. In the FeatureActivated method, add:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;DeployToHive(properties);&lt;/div&gt;&lt;br /&gt;21. In the FeatureDeactivating method, add:&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;RemoveFromHive(properties);&lt;/div&gt;&lt;br /&gt;22. Below the FeatureUninstalling method, add the following 4 methods (DeployToHive, RemoveFromHive, GetHiveFile, GetLocalDirectory):&lt;pre face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;private void DeployToHive(SPFeatureReceiverProperties properties)&lt;br /&gt;{&lt;br /&gt;    //loop through each server in the farm&lt;br /&gt;    foreach (SPServer server in properties.Definition.Farm.Servers)&lt;br /&gt;    {&lt;br /&gt;        //if the current server is a WebFrontEnd server&lt;br /&gt;        if (server.Role == SPServerRole.WebFrontEnd || server.Role == SPServerRole.Application &lt;br /&gt;            || server.Role == SPServerRole.SingleServer)&lt;br /&gt;        {&lt;br /&gt;            string localFilePath = GetLocalDirectory(properties.Definition.RootDirectory,&lt;br /&gt;                     server.Name);&lt;br /&gt;            localDirectory = new DirectoryInfo(localFilePath);&lt;br /&gt;&lt;br /&gt;            foreach (FileInfo fileinfo in localDirectory.GetFiles("*", &lt;br /&gt;                     SearchOption.AllDirectories))&lt;br /&gt;            {&lt;br /&gt;                //if current file in feature exists in 12 hive on current server&lt;br /&gt;                if (File.Exists(GetHiveFile(fileinfo.FullName, server.Name)) &lt;br /&gt;                    &amp;&amp; (!fileinfo.FullName.Contains(".hivebackup")))&lt;br /&gt;                    // backup file from 12 hive to feature&lt;br /&gt;                    File.Copy(GetHiveFile(fileinfo.FullName, server.Name),&lt;br /&gt;                              fileinfo.FullName + ".hivebackup", true);&lt;br /&gt;&lt;br /&gt;                //if current file in feature exists&lt;br /&gt;                if (File.Exists(fileinfo.FullName) &amp;&amp; &lt;br /&gt;                    (!fileinfo.FullName.Contains(".hivebackup")))&lt;br /&gt;                    // copy local file to the 12 hive&lt;br /&gt;                    File.Copy(fileinfo.FullName, GetHiveFile(fileinfo.FullName, server.Name), &lt;br /&gt;                               true);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;private void RemoveFromHive(SPFeatureReceiverProperties properties)&lt;br /&gt;{&lt;br /&gt;    //loop through each server in the farm&lt;br /&gt;    foreach (SPServer server in properties.Definition.Farm.Servers)&lt;br /&gt;    {&lt;br /&gt;        //if the current server is a WebFrontEnd server&lt;br /&gt;        if (server.Role == SPServerRole.WebFrontEnd&lt;br /&gt;           || server.Role == SPServerRole.Application&lt;br /&gt;           || server.Role == SPServerRole.SingleServer)&lt;br /&gt;        {&lt;br /&gt;            string localFilePath = GetLocalDirectory(properties.Definition.RootDirectory,&lt;br /&gt;                server.Name);&lt;br /&gt;            localDirectory = new DirectoryInfo(localFilePath);&lt;br /&gt;&lt;br /&gt;            foreach (FileInfo fileinfo in localDirectory.GetFiles("*",&lt;br /&gt;                 SearchOption.AllDirectories))&lt;br /&gt;            {&lt;br /&gt;                //if current backup file in feature exists in 12 hive&lt;br /&gt;                if (File.Exists(fileinfo.FullName + ".hivebackup"))&lt;br /&gt;                    // copy hivebackup file back to the 12 hive&lt;br /&gt;                    File.Copy(fileinfo.FullName + ".hivebackup",&lt;br /&gt;                              GetHiveFile(fileinfo.FullName, server.Name), true);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;string GetHiveFile(string fileName, string serverName)&lt;br /&gt;{&lt;br /&gt;    if (fileName.StartsWith(localDirectory.FullName))&lt;br /&gt;    { // file is local&lt;br /&gt;        string p = @"\\" + serverName&lt;br /&gt;                   + @"\C$\Program Files\Common Files\Microsoft Shared\web server extensions\12"&lt;br /&gt;                   + fileName.Remove(0, localDirectory.FullName.Length);&lt;br /&gt;        return p;&lt;br /&gt;    }&lt;br /&gt;    throw new Exception("Filepath doesn't point to correct local hive!");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;string GetLocalDirectory(string filePath, string serverName)&lt;br /&gt;{&lt;br /&gt;    return @"\\" + serverName + @"\" + @filePath.Replace(":", "$") + @"\12";&lt;br /&gt;&lt;br /&gt;    //write a class that writes exceptions to the event log&lt;br /&gt;    throw new Exception("Filepath doesn't point to correct local hive!");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now we need to create the appropriate 12-Hive file structure for the feature in Solution Explorer. Any 12-Hive files we want to update will have their newer versions placed in the file structure we are about to create.&lt;br /&gt;&lt;br /&gt;23. In Solution Explorer, right-click project HiveUpdates, then click Add -&gt; New Item... -&gt; SharePoint -&gt; Template.&lt;br /&gt;&lt;br /&gt;24. You will notice a folder called Templates was created in the Solution Explorer. Delete the default file TemplateFile1.txt.&lt;br /&gt;&lt;br /&gt;25. Under Templates, create a folder called FEATURES.&lt;br /&gt;&lt;br /&gt;26. Under FEATURES, create a folder called HiveUpdates.&lt;br /&gt;&lt;br /&gt;So far, we have created 12/TEMPLATE/FEATURES/HiveUpdates. This is the location in the 12-Hive where that feature's feature.xml gets deployed to. But now, we must create another 12-Hive structure under this folder. This is where our newer files will be deployed to when the solution is initially deployed. When the feature is activated, this is where the above code will store backups of the original files from the 12-Hive, as well as the source from which to overwrite the 12-Hive files with.&lt;br /&gt;&lt;br /&gt;27. Under HiveUpdates, create a folder called 12.&lt;br /&gt;&lt;br /&gt;28. Under 12, create a folder called TEMPLATE.&lt;br /&gt;&lt;br /&gt;29. Under TEMPLATE, create a folder called LAYOUTS.&lt;br /&gt;&lt;br /&gt;30. Copy application.master in here. Either make some small change to it or at the very least change the date on the file so its different from what it is in the 12-Hive. Be sure to right-click application.master and click Include In Project if it is not already included.&lt;br /&gt;&lt;br /&gt;31. In WSP View, double-click feature.xml.&lt;br /&gt;&lt;br /&gt;32. Change the value of Title from Feature1 to HiveUpdates.&lt;br /&gt;&lt;br /&gt;33. Verify Scope is set to Farm.&lt;br /&gt;&lt;br /&gt;33. After the title or scope, add: ActivateOnDefault="false"&lt;br /&gt;&lt;br /&gt;34. Verify your manifest was created correctly. In WSP View, Under HiveUpdates, you should see manifest.xml. Double-click manifest.xml and verify your file path for application.master is FEATURES\HiveUpdates\12\TEMPLATE\LAYOUTS.&lt;br /&gt;&lt;br /&gt;35. click Build -&gt; Rebuild Solution&lt;br /&gt;&lt;br /&gt;36. click Build -&gt; Package Solution&lt;br /&gt;&lt;br /&gt;The solution package that you just created in the above step, HiveUpdates.wsp, was created in your project's bin\debug folder.&lt;br /&gt;&lt;br /&gt;To deploy the solution:&lt;br /&gt;&lt;br /&gt;37. create folder d:\deploy&lt;br /&gt;&lt;br /&gt;38. copy HiveUpdates.wsp from project's bin\debug folder to d:\deploy&lt;br /&gt;&lt;br /&gt;39. In d:\deploy, create two batch files, add_hiveupdates.bat and remove_hiveupdates.bat.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;add_hiveupdates.bat:&lt;/b&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;c:&lt;br /&gt;&lt;br /&gt;    cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN&lt;br /&gt;&lt;br /&gt;    stsadm -o addsolution -filename d:\deploy\HiveUpdates\HiveUpdates.wsp&lt;br /&gt;&lt;br /&gt;    stsadm -o deploysolution -name HiveUpdates.wsp -immediate  -allowGacDeployment&lt;br /&gt;&lt;br /&gt;    pause&lt;br /&gt;&lt;br /&gt;    stsadm -o activatefeature -name HiveUpdates&lt;br /&gt;&lt;br /&gt;    d:&lt;/div&gt;&lt;br /&gt;&lt;b&gt;remove_hiveupdates.bat:&lt;/b&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149); font-size: 14px;"&gt;c:&lt;br /&gt;&lt;br /&gt;    cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN&lt;br /&gt;&lt;br /&gt;    stsadm -o deactivatefeature -name HiveUpdates&lt;br /&gt;&lt;br /&gt;    stsadm -o retractsolution -name HiveUpdates.wsp -immediate&lt;br /&gt;&lt;br /&gt;    pause&lt;br /&gt;&lt;br /&gt;    stsadm -o deletesolution -name HiveUpdates.wsp&lt;br /&gt;&lt;br /&gt;    d:&lt;/div&gt;&lt;br /&gt;40. in d:\deploy, type add_hiveupdates.wsp and hit enter&lt;br /&gt;&lt;br /&gt;41. verify solution was deployed in Central Administration -&gt; Operations -&gt; Solution Management&lt;br /&gt;&lt;br /&gt;42. verify files were added to 12-Hive!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-5937309416855013178?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/5937309416855013178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/03/update-12-hive-through-feature.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/5937309416855013178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/5937309416855013178'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/03/update-12-hive-through-feature.html' title='Update 12 Hive Through A Feature'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-7986325782088998883</id><published>2010-06-05T16:07:00.009-04:00</published><updated>2010-06-10T08:40:35.435-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='update 12-hive'/><title type='text'>Add New Files To 12-Hive Through A SharePoint  Solution</title><content type='html'>As we got our SharePoint environment up and running, we needed to have the ability to make changes to our 12-Hive. We also wanted to have the flexibility to undo the changes and restore the 12-Hive to the way it was originally.&lt;br /&gt;&lt;br /&gt;This post discusses the scenario where you need to add new files to the 12-Hive that do not already exist, such as new images or layouts. I call this solution &lt;b&gt;HiveAdditions&lt;/b&gt;. If you have a need to update files that already exist in the 12-Hive, check out my other article &lt;a href="http://simplesharepointsolutions.blogspot.com/2010/03/update-12-hive-through-feature.html"&gt;Update 12 Hive Through A Feature&lt;/a&gt;.  I call that solution &lt;b&gt;HiveUpdates&lt;/b&gt;. We decided it would be best for us to separate these two types of Hive changes into two separate solutions. &lt;br /&gt;&lt;br /&gt;This solution, &lt;b&gt;HiveAdditions&lt;/b&gt;, simply allows us to add files into the 12-Hive upon a deployment. When we retract this solution, it simply removes all those files. We liked this approach because it allows us to have this solution as the one place for all new 12-Hive files. If we needed to add new files, we retract this solution, add the new files to the solution, then deploy it again.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1. In Visual Studio 2008, File -&gt; New -&gt; Project&lt;br /&gt;&lt;br /&gt;2. Under Visual C#, pick SharePoint, then Empty&lt;br /&gt;&lt;br /&gt;3. For Name, type: HiveAdditions&lt;br /&gt;&lt;br /&gt;4. For Location, type: D:\development&lt;br /&gt;&lt;br /&gt;5. Click OK&lt;br /&gt;&lt;br /&gt;6. Select Full Trust (Deploy to GAC) and click OK&lt;br /&gt;&lt;br /&gt;7. In Solution Explorer, right-click project HiveAdditions, then click Add -&gt; New Items... -&gt; SharePoint -&gt; Root File&lt;br /&gt;&lt;br /&gt;8. Leave Name alone and click Add&lt;br /&gt;&lt;br /&gt;9. Under RootFiles (which now appears in Solution Explorer), delete RootFile1&lt;br /&gt;&lt;br /&gt;Under RootFiles, we must mirror the 12-Hive locations where we want to add the new files. &lt;br /&gt;&lt;br /&gt;Let's add image files under TEMPLATE/IMAGES.&lt;br /&gt;&lt;br /&gt;10. Right-click RootFiles, click Add -&gt; New Folder and name it TEMPLATE.&lt;br /&gt;&lt;br /&gt;11. Right-click TEMPLATE, click Add -&gt; New Folder and name it IMAGES.&lt;br /&gt;&lt;br /&gt;12. Drag and drop your images into IMAGES folder. Be sure to right-click newly added files and click Include In Project if they are not already included.&lt;br /&gt;&lt;br /&gt;13. Verify your manifest was created correctly. Click View -&gt; Other Windows -&gt; WSP View. Under HiveAdditions, you should see manifest.xml, then under that, RootFiles -&gt; TEMPLATE -&gt; IMAGES followed by the files you added. Double-click manifest.xml and verify your file paths are under TEMPLATE\IMAGES\file1.jpg, etc.&lt;br /&gt;&lt;br /&gt;14. click Build -&gt; Rebuild Solution&lt;br /&gt;&lt;br /&gt;15. click Build -&gt; Package Solution&lt;br /&gt;&lt;br /&gt;The solution package that you just created in the above step, HiveAdditions.wsp, was created in your project's bin\debug folder.&lt;br /&gt;&lt;br /&gt;To deploy the solution:&lt;br /&gt;&lt;br /&gt;16. create folder d:\deploy&lt;br /&gt;&lt;br /&gt;17. copy HiveAdditions.wsp from project's bin\debug folder to d:\deploy&lt;br /&gt;&lt;br /&gt;18. In d:\deploy, create two batch files, add_hiveadditions.bat and remove_hiveadditions.bat.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;add_hiveadditions.bat:&lt;/b&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;c:&lt;br /&gt;&lt;br /&gt;    cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN&lt;br /&gt;&lt;br /&gt;    stsadm -o addsolution -filename  d:\deploy\HiveAdditions\HiveAdditions.wsp&lt;br /&gt;&lt;br /&gt;    stsadm -o deploysolution -name HiveAdditions.wsp -immediate  -allowGacDeployment&lt;br /&gt;&lt;br /&gt;    d:&lt;/div&gt;&lt;b&gt;remove_hiveadditions.bat:&lt;/b&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;c:&lt;br /&gt;&lt;br /&gt;    cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN&lt;br /&gt;&lt;br /&gt;    stsadm -o retractsolution -name HiveAdditions.wsp -immediate&lt;br /&gt;&lt;br /&gt;    pause&lt;br /&gt;&lt;br /&gt;    stsadm -o deletesolution -name HiveAdditions.wsp&lt;br /&gt;&lt;br /&gt;    d:&lt;/div&gt;19. in d:\deploy, type add_hiveadditions.wsp and hit enter&lt;br /&gt;&lt;br /&gt;20. verify solution was deployed in Central Administration -&gt; Operations -&gt; Solution Management&lt;br /&gt;&lt;br /&gt;21. verify files were added to 12-Hive!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-7986325782088998883?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/7986325782088998883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/06/add-new-files-to-12-hive-through_2091.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/7986325782088998883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/7986325782088998883'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2010/06/add-new-files-to-12-hive-through_2091.html' title='Add New Files To 12-Hive Through A SharePoint  Solution'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-6553718671138486695</id><published>2009-08-25T23:00:00.020-04:00</published><updated>2009-08-26T11:11:58.327-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deployment'/><category scheme='http://www.blogger.com/atom/ns#' term='list template'/><category scheme='http://www.blogger.com/atom/ns#' term='feature'/><title type='text'>How to deploy SharePoint list templates</title><content type='html'>Ok, so after I had packaged up three web parts into their own features in a solution (wsp) file, I had one more issue to address: those three web parts were dependent on lists created from two list templates. So I needed to also package up those list templates as features in the solution file.&lt;br /&gt;&lt;br /&gt;After finding countless pages that explain how to do it the hard way (and there are many hard ways), I finally realized one of my favorite sites showed me the easiest, best way, by far. Jeremy Thake of &lt;a href="http://www.sharepointdevwiki.com/display/public/Welcome"&gt;SharePointDevWiki &lt;/a&gt; created a video showing how its done. Jeremy is the man downunder! Excuse me, the bloke downunder.&lt;br /&gt;&lt;br /&gt;In addition, I also needed to package up two list templates into the solution package, not just one as is done in the &lt;a href="http://www.sharepointdevwiki.com/display/public/SPSource"&gt;video&lt;/a&gt;. As with most stuff SharePoint, once you know how to do something, its extremely easy to do. &lt;br /&gt;&lt;br /&gt;At a high level, here is what we will do:&lt;ol&gt;&lt;li&gt;Create some lists in your SharePoint site.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Install SPSource from CodePlex and use it to reverse engineer the lists you created on your SharePoint site back into list templates, storing the necessary files that make up the list templates into a Visual Studio project.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use BuildWSP to build your WSP!&lt;/li&gt;&lt;/ol&gt;Note: in this example, I will create a new Visual Studio project. However, I want to add the reverse-engineered list templates into my existing project with my web parts. To do this, all I have to do is copy the contents of my FEATURES folder from my new Visual Studio project (where I reverse engineered my list templates) into my existing Visual Studio project's FEATURES folder (where my web parts already are). When I run BuildWSP, it will just simply see the new list template features and add those to the .wsp. I will not show the copy in the steps below because I don't know if you need to add your list templates to a pre-existing project, but like I said, just copy the list templates from the FEATURES folder if you want to do this.&lt;br /&gt;&lt;br /&gt;Ok, so now for a little bit of detail.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;In your SharePoint site, create two lists. I will call one Contact List Template and the other Sent Emails Template in this example. Add some columns to them.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Download &lt;a href="http://www.codeplex.com/SPSource"&gt;SPSource&lt;/a&gt; from CodePlex. I put spsource.exe in a folder called d:\deploy, but just put it somewhere easy to get to.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;If you haven't already downloaded and installed &lt;a href="http://wspbuilder.codeplex.com/"&gt;WSPBuilder&lt;/a&gt;, do it. WSPBuilder will be added under the Tools menu in Visual Studio.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Create the Visual Studio project that will be the destination for the reverse-engineered list templates.&lt;ol type="a"&gt;&lt;li&gt;In Visual Studio, click File -&gt; New -&gt; Project -&gt; WSPBuilder -&gt; WSPBuilder Project&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Name it MyListTemplates and click OK.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add a feature for each list template you wish to reverse engineer. (Note: Trying to add both list templates to the same feature has proven to be problematic. If you add them to the same feature, when you deploy the feature and create lists from the list templates, all the lists you create will only use the definition from the first list template that was deployed, even if you create a list based on the second list template. Therefore, create one feature for each list template!)&lt;br /&gt;&lt;ol type="a"&gt;&lt;li&gt;In the Solution Explorer, right-click the project, then click Add -&gt; New Item -&gt; WSPBuilder -&gt; Blank Feature&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Name it ContactListTemplate and click Add.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the Feature Settings dialog, change the Scope to Site and click OK.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Do a. through c. again, but this time name the feature SentEmailsTemplate.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Notice both features have been added under /12/TEMPLATE/FEATURES/ in your Solution Explorer. Each contains an elements.xml and feature.xml.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Create a .spsource file for each feature. These files tell SPSource what lists to reverse engineer into list templates.&lt;br /&gt;&lt;ol type="a"&gt;&lt;li&gt;In Solution Explorer, right-click the ContactListTemplate feature, then click Add -&gt; New Item -&gt; Common -&gt; XML File, name it contactlist.spsource and click Add.&lt;/il&gt;&lt;br /&gt;&lt;li&gt;In Solution Explorer, right-click the SentEmailsTemplate feature, then click Add -&gt; New Item -&gt; Common -&gt; XML File, name it contactlist.spsource and click Add.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Copy the contents of elements.xml into both of these .spsource files.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In contactlist.spsource, add the following inside the Elements tag:&lt;br /&gt;   &lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;&amp;lt;ListTemplate Name="Contact List Template" /&amp;gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;In sentemails.spsource, add the following inside the Elements tag:&lt;br /&gt;   &lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;&amp;lt;ListTemplate Name="Sent Emails Template" /&amp;gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;In your project's folder, create a text file called SPSource.cmd with these contents:&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;@ECHO OFF&lt;br /&gt;&lt;br /&gt;SET SPSOURCE="d:\deploy\SPSource.exe"&lt;br /&gt;SET DEVSITEURL="http://sharepoint2007:1001/marketing"&lt;br /&gt;&lt;br /&gt;%SPSOURCE% -designsite %DEVSITEURL%&lt;br /&gt;&lt;br /&gt;PAUSE&lt;br /&gt;&lt;/div&gt;Be sure to set DEVSITEURL to your SharePoint site where you created the lists back in step 1.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Double-click SPSource.cmd so it runs. If you get an error message saying something like "Method not found...SPOpenBinaryOptions...), you need to install &lt;a href="http://www.microsoft.com/downloadS/details.aspx?familyid=4191A531-A2E9-45E4-B71E-5B0B17108BD2&amp;displaylang=en"&gt;Windows SharePoint Services 3.0 Service Pack 1 (SP1)&lt;/a&gt;. This is because SPSource requires methods that are in WSS 3.0 SP1.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;In Solution Explorer, click Show All Files. Under the two features, you'll see a new folder under each with the same name. Right-click the new folders and click Include In Project.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Build the project.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Create the wsp solution file by clicking Tools -&gt; WSP Builder -&gt; Build WSP.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;You can now deploy MyListTemplates.wsp with stsadm as you normally deploy wsps! Use addsolution, deploysolution, and activatefeature to deploy however you want to deploy it (ie using GAC or CAS, etc).&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-6553718671138486695?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/6553718671138486695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-deploy-sharepoint-list-templates.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6553718671138486695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6553718671138486695'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-deploy-sharepoint-list-templates.html' title='How to deploy SharePoint list templates'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-4805553951140331708</id><published>2009-08-20T23:03:00.008-04:00</published><updated>2009-08-20T23:19:03.886-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 6 - Add task notification emails</title><content type='html'>Here in Part 6, we'll add email notification events so when a task is assigned, approved, or rejected, the appropriate emails are sent to the proper people. This part assumes you have completed part 4.&lt;br /&gt;&lt;pre style="font-family: ariel;"&gt;&lt;br /&gt;1. go back to the design surface and double-click PeerReviewerApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;2. drag and drop a SendEmail activity below hlogPeerReviewerApprovalTaskCreated&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. Name: sendPeerReviewerApprovalTaskEmail&lt;br /&gt;    b. Body: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;             remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    c. CC: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    d. CorrelationToken: choose workflowToken from dropdownlist&lt;br /&gt;    e. Subject: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;                remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    f. To: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    g. MethodInvoking: type PeerReviewerApprovalTaskEmail and hit enter&lt;br /&gt;    h. You will be taken to the code stub for PeerReviewerApprovalTaskEmail. Copy and paste the following code:&lt;br /&gt;&lt;br /&gt;        'get PeerReviewer's email address&lt;br /&gt;        Dim PeerReviewerObject As SPUser = GetUserObject(PeerReviewer)&lt;br /&gt;        Dim PeerReviewerEmail = PeerReviewerObject.Email&lt;br /&gt;&lt;br /&gt;        'get CC's email address&lt;br /&gt;        Dim CCObject As SPUser = GetUserObject(CC)&lt;br /&gt;        Dim CCEmail = CCObject.Email&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        sendPeerReviewerApprovalTaskEmail_To = PeerReviewerEmail&lt;br /&gt;        sendPeerReviewerApprovalTaskEmail_CC = CCEmail&lt;br /&gt;        sendPeerReviewerApprovalTaskEmail_Subject = "Approval of " &amp; workflowProperties.Item.File.Name &amp; " has been assigned to you."&lt;br /&gt;        sendPeerReviewerApprovalTaskEmail_Body = "&amp;lt;span style='font-family: arial; font-size: medium'&amp;gt;" &amp; _&lt;br /&gt;                                                 "Task assigned by " &amp; workflowProperties.OriginatorUser.Name &amp; " on " &amp; DateTime.Now &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;span style='font-family: arial; font-size: x-small'&amp;gt;" &amp; _&lt;br /&gt;                                                 "Due by " &amp; DateTime.Today &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "Instructions: &amp;lt;br&amp;gt;" &amp; Instructions &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "Please approve " &amp; workflowProperties.Item.File.Name &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "To complete this task:" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. Review " &amp; "&amp;lt;a href='" &amp; workflowProperties.SiteUrl &amp; "\" &amp; workflowProperties.ItemUrl &amp; "'&amp;gt;" &amp; workflowProperties.Item.File.Name &amp; "&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. Use the &amp;lt;b&amp;gt;Edit this task&amp;lt;/b&amp;gt; button to Approve/Reject the document." &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "To view this workflow's history, click " &amp; "&amp;lt;a href=" &amp; workflowProperties.SiteUrl &amp; "/_layouts/WrkStat.aspx?List=" &amp; workflowProperties.ListId.ToString &amp; "&amp;WorkflowInstanceID=" &amp; workflowProperties.WorkflowId.ToString &amp; "&amp;gt;here&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;"&lt;br /&gt;&lt;br /&gt;        'if this is the first time we have gone to the peer reviewer state, do not show comments&lt;br /&gt;        '  in email&lt;br /&gt;        If ArrivedFromInitiatorState = True Then&lt;br /&gt;            'get the comments entered by the initiator&lt;br /&gt;            Dim Comments As String = onInitiatorApprovalChanged.AfterProperties.ExtendedProperties("txtComments").ToString&lt;br /&gt;&lt;br /&gt;            sendPeerReviewerApprovalTaskEmail_Body = sendPeerReviewerApprovalTaskEmail_Body &amp; _&lt;br /&gt;                                                 "&amp;lt;span style='font-family: arial; font-size: x-small'&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;font color=red&amp;gt;Comments:&amp;lt;/font&amp;gt; &amp;lt;br&amp;gt;" &amp; Comments &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;"&lt;br /&gt;        End If&lt;br /&gt;&lt;br /&gt;3. go back to the design surface and double-click InitiatorApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;4. drag and drop a SendEmail activity below hlogPeerReviewerApprovalTaskCreated&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. Name: sendInitiatorApprovalTaskEmail&lt;br /&gt;    b. Body: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;             remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    c. CC: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    d. CorrelationToken: choose workflowToken from dropdownlist&lt;br /&gt;    e. Subject: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;                remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    f. To: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    g. MethodInvoking: type InitiatorApprovalTaskEmail and hit enter&lt;br /&gt;    h. You will be taken to the code stub for PeerReviewerApprovalTaskEmail. Copy and paste the following code:&lt;br /&gt;&lt;br /&gt;        'get Initiator's email address&lt;br /&gt;        Dim InitiatorObject As SPUser = GetUserObject(workflowProperties.Originator)&lt;br /&gt;        Dim InitiatorEmail = InitiatorObject.Email&lt;br /&gt;&lt;br /&gt;        'get CC's email address&lt;br /&gt;        Dim CCObject As SPUser = GetUserObject(CC)&lt;br /&gt;        Dim CCEmail = CCObject.Email&lt;br /&gt;&lt;br /&gt;        'get PeerReviewer's name&lt;br /&gt;        Dim PeerReviewerObject As SPUser = GetUserObject(PeerReviewer)&lt;br /&gt;        Dim PeerReviewerName = PeerReviewerObject.Name&lt;br /&gt;&lt;br /&gt;        'get the comments entered by the peer reviewer&lt;br /&gt;        Dim Comments As String = onPeerReviewerApprovalChanged.AfterProperties.ExtendedProperties("txtComments").ToString&lt;br /&gt;&lt;br /&gt;        sendInitiatorApprovalTaskEmail_To = InitiatorEmail&lt;br /&gt;        sendInitiatorApprovalTaskEmail_CC = CCEmail&lt;br /&gt;        sendInitiatorApprovalTaskEmail_Subject = "Approval of " &amp; workflowProperties.Item.File.Name &amp; " has been assigned to you."&lt;br /&gt;        sendInitiatorApprovalTaskEmail_Body = "&amp;lt;span style='font-family: arial; font-size: medium'&amp;gt;" &amp; _&lt;br /&gt;                                                 "Task assigned by " &amp; PeerReviewerName &amp; " on " &amp; DateTime.Now &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;span style='font-family: arial; font-size: x-small'&amp;gt;" &amp; _&lt;br /&gt;                                                 "Due by " &amp; DateTime.Today &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "Instructions: &amp;lt;br&amp;gt;" &amp; Instructions &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "Please approve " &amp; workflowProperties.Item.File.Name &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "To complete this task:" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. Review " &amp; "&amp;lt;a href='" &amp; workflowProperties.SiteUrl &amp; "\" &amp; workflowProperties.ItemUrl &amp; "'&amp;gt;" &amp; workflowProperties.Item.File.Name &amp; "&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. Use the &amp;lt;b&amp;gt;Edit this task&amp;lt;/b&amp;gt; button to Approve/Reject the document." &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "To view this workflow's history, click " &amp; "&amp;lt;a href=" &amp; workflowProperties.SiteUrl &amp; "/_layouts/WrkStat.aspx?List=" &amp; workflowProperties.ListId.ToString &amp; "&amp;WorkflowInstanceID=" &amp; workflowProperties.WorkflowId.ToString &amp; "&amp;gt;here&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;font color=red&amp;gt;Comments:&amp;lt;/font&amp;gt; &amp;lt;br&amp;gt;" &amp; Comments &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;"&lt;br /&gt;&lt;br /&gt;5. go back to the design surface and double-click PeerReviewerApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;6. drag and drop a SendEmail activity between hlogPeerReviewerApproved and hlogWorkflowCompleted&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. Name: sendPeerReviewerApprovedEmail&lt;br /&gt;    b. Body: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;             remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    c. CC: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    d. CorrelationToken: choose workflowToken from dropdownlist&lt;br /&gt;    e. Subject: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;                remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    f. To: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    g. MethodInvoking: type InitiatorApprovalTaskEmail and hit enter&lt;br /&gt;    h. You will be taken to the code stub for PeerReviewerApprovedEmail. Copy and paste the following code:&lt;br /&gt;&lt;br /&gt;        'get Initiator's email address&lt;br /&gt;        Dim InitiatorObject As SPUser = GetUserObject(workflowProperties.Originator)&lt;br /&gt;        Dim InitiatorEmail = InitiatorObject.Email&lt;br /&gt;&lt;br /&gt;        'get CC's email address&lt;br /&gt;        Dim CCObject As SPUser = GetUserObject(CC)&lt;br /&gt;        Dim CCEmail = CCObject.Email&lt;br /&gt;&lt;br /&gt;        'get PeerReviewer's name&lt;br /&gt;        Dim PeerReviewerObject As SPUser = GetUserObject(PeerReviewer)&lt;br /&gt;        Dim PeerReviewerName = PeerReviewerObject.Name&lt;br /&gt;&lt;br /&gt;        sendPeerReviewerApprovedEmail_To = InitiatorEmail&lt;br /&gt;        sendPeerReviewerApprovedEmail_CC = CCEmail&lt;br /&gt;        sendPeerReviewerApprovedEmail_Subject = workflowProperties.Item.File.Name &amp; " has been approved by " &amp; PeerReviewerName &amp; ". Peer Review Complete."&lt;br /&gt;        sendPeerReviewerApprovedEmail_Body = "&amp;lt;span style='font-family: arial; font-size: medium'&amp;gt;" &amp; _&lt;br /&gt;                                                 workflowProperties.Item.File.Name &amp; " was approved by " &amp; PeerReviewerName &amp; " on " &amp; DateTime.Now &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "Peer Review Complete" &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;span style='font-family: arial; font-size: x-small'&amp;gt;" &amp; _&lt;br /&gt;                                                 "To view this workflow's history, click " &amp; "&amp;lt;a href=" &amp; workflowProperties.SiteUrl &amp; "/_layouts/WrkStat.aspx?List=" &amp; workflowProperties.ListId.ToString &amp; "&amp;WorkflowInstanceID=" &amp; workflowProperties.WorkflowId.ToString &amp; "&amp;gt;here&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;"&lt;br /&gt;&lt;br /&gt;7. drag and drop a SendEmail activity between hlogPeerReviewerRejected and setStateInitiatorApproval&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. Name: sendPeerReviewerRejectedEmail&lt;br /&gt;    b. Body: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;             remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    c. CC: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    d. CorrelationToken: choose workflowToken from dropdownlist&lt;br /&gt;    e. Subject: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;                remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    f. To: click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;           remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;    g. MethodInvoking: type InitiatorApprovalTaskEmail and hit enter&lt;br /&gt;    h. You will be taken to the code stub for PeerReviewerRejectedEmail. Copy and paste the following code:&lt;br /&gt;&lt;br /&gt;        'get Initiator's email address&lt;br /&gt;        Dim InitiatorObject As SPUser = GetUserObject(workflowProperties.Originator)&lt;br /&gt;        Dim InitiatorEmail = InitiatorObject.Email&lt;br /&gt;&lt;br /&gt;        'get CC's email address&lt;br /&gt;        Dim CCObject As SPUser = GetUserObject(CC)&lt;br /&gt;        Dim CCEmail = CCObject.Email&lt;br /&gt;&lt;br /&gt;        'get PeerReviewer's name&lt;br /&gt;        Dim PeerReviewerObject As SPUser = GetUserObject(PeerReviewer)&lt;br /&gt;        Dim PeerReviewerName = PeerReviewerObject.Name&lt;br /&gt;&lt;br /&gt;        sendPeerReviewerRejectedEmail_To = InitiatorEmail&lt;br /&gt;        sendPeerReviewerRejectedEmail_CC = CCEmail&lt;br /&gt;        sendPeerReviewerRejectedEmail_Subject = workflowProperties.Item.File.Name &amp; " has been rejected by " &amp; PeerReviewerName&lt;br /&gt;        sendPeerReviewerRejectedEmail_Body = "&amp;lt;span style='font-family: arial; font-size: medium'&amp;gt;" &amp; _&lt;br /&gt;                                                 workflowProperties.Item.File.Name &amp; " was rejected by " &amp; PeerReviewerName &amp; " on " &amp; DateTime.Now &amp; _&lt;br /&gt;                                                 "&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;" &amp; _&lt;br /&gt;                                                 "&amp;lt;span style='font-family: arial; font-size: x-small'&amp;gt;" &amp; _&lt;br /&gt;                                                 "To view this workflow's history, click " &amp; "&amp;lt;a href=" &amp; workflowProperties.SiteUrl &amp; "/_layouts/WrkStat.aspx?List=" &amp; workflowProperties.ListId.ToString &amp; "&amp;WorkflowInstanceID=" &amp; workflowProperties.WorkflowId.ToString &amp; "&amp;gt;here&amp;lt;/a&amp;gt;" &amp; "." &amp; _&lt;br /&gt;                                                 "&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Posts in this series:&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html"&gt;Part 1: Introduction&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;Part 2: Create the initiation form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;Part 3: Create the task form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;Part 4: Create the state machine workflow&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;Part 5: Add workflow history logging&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;Part 6: Add task notification emails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-4805553951140331708?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/4805553951140331708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/4805553951140331708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/4805553951140331708'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html' title='How to create a SharePoint State Machine Workflow: Part 6 - Add task notification emails'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-1545273020451354671</id><published>2009-08-20T22:55:00.007-04:00</published><updated>2009-08-20T23:05:38.822-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 5 - Add workflow history logging</title><content type='html'>Here in Part 5, We'll add history logging to the workflow. This is really useful as it allows the user to see messages about the status of the workflow, such as who approved it or rejected it. This part assumes you have completed part 4.&lt;br /&gt;&lt;pre style="font-family: ariel;"&gt;&lt;br /&gt;1. add this function to the code:&lt;br /&gt;&lt;br /&gt;    Private Function GetUserObject(ByVal accountID As String) As SPUser&lt;br /&gt;        If accountID.IndexOf("\") &gt; 0 Then&lt;br /&gt;            Dim user As SPUser = Me.workflowProperties.Web.SiteUsers(accountID)&lt;br /&gt;            Return user&lt;br /&gt;        Else&lt;br /&gt;            Dim user As SPUser = Me.workflowProperties.Web.SiteUsers("OSS\" + accountID)&lt;br /&gt;            Return user&lt;br /&gt;        End If&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;2. go back to the design surface and double-click InitialStateActivities to drill down into it&lt;br /&gt;&lt;br /&gt;3. drag and drop a LogToHistoryListActivity activity between onWorkflowActivated and setStatePeerReviewerApproval1&lt;br /&gt;   and set the following properties:&lt;br /&gt;   a. name: hlogWorkflowStarted&lt;br /&gt;   b. EventId: WorkflowStarted&lt;br /&gt;   c. HistoryDescription: Workflow started.&lt;br /&gt;   d. HistoryOutcome: create property hlogWorkflowStarted_HistoryOutcome&lt;br /&gt;   e. UserId: create property hlogWorkflowStarted_UserId&lt;br /&gt;&lt;br /&gt;4. double-click the onWorkflowActivated activity to be taken to its code stub&lt;br /&gt;&lt;br /&gt;5. copy and paste the following code to have 1. the workflow's originator assigned to the UserId property of the&lt;br /&gt;   logToHistory activity, and 2. the instructions entered by the originator assigned to the HistoryOutcome property:&lt;br /&gt;&lt;br /&gt;      'assign workflow's executor to the LogToHistory activity's user id&lt;br /&gt;      Dim executor As SPUser = GetUserObject(workflowProperties.Originator)&lt;br /&gt;      hlogWorkflowStarted_UserId = executor.ID&lt;br /&gt;&lt;br /&gt;      'assign the instructions to the HistoryOutcome property&lt;br /&gt;      hlogWorkflowStarted_HistoryOutcome = "Instructions: " &amp; initform.txtInstructions&lt;br /&gt;&lt;br /&gt;6. go back to the design surface and double-click InitiatorApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;7. drag and drop a LogToHistoryListActivity activity below createInitiatorApprovalTask&lt;br /&gt;   and set the following properties:&lt;br /&gt;   a. name: hlogInitiatorApprovalTaskCreated&lt;br /&gt;   b. EventId: TaskCreated&lt;br /&gt;   c. HistoryDescription: Initiator approval task created. Awaiting approval.&lt;br /&gt;   d. UserId: 0&lt;br /&gt;&lt;br /&gt;8. go back to the design surface and double-click InitiatorApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;9. drag and drop a LogToHistoryListActivity activity between ifInitiatorApproved and setStatePeerReviewerApproval&lt;br /&gt;   and set the following properties:&lt;br /&gt;   a. name: hlogInitiatorApproved&lt;br /&gt;   b. EventId: TaskCompleted&lt;br /&gt;   c. HistoryDescription: Initiator approved.&lt;br /&gt;   d. HistoryOutcome: create property hlogInitiatorApproved_HistoryOutcome&lt;br /&gt;   e. UserId: create property hlogInitiatorApproved_UserId&lt;br /&gt;&lt;br /&gt;10. click on the onInitiatorApprovalChanged activity and set the following property&lt;br /&gt;    a. Executor: create property onInitiatorApprovalChanged_Executor&lt;br /&gt;&lt;br /&gt;11. double-click onInitiatorApprovalChanged activity to be taken to its code stub&lt;br /&gt;&lt;br /&gt;12. copy and paste the following code to have the task's executor assigned to the UserId property of the&lt;br /&gt;    logToHistory activity:&lt;br /&gt;&lt;br /&gt;      'assign task's executor to the LogToHistory activity's user id&lt;br /&gt;      Dim executor As SPUser = GetUserObject(onInitiatorApprovalChanged_Executor)&lt;br /&gt;      hlogInitiatorApproved_UserId = executor.ID&lt;br /&gt;&lt;br /&gt;      'assign the task's comments to the HistoryOutcome property&lt;br /&gt;      hlogInitiatorApproved_HistoryOutcome = "Comments: " &amp; onInitiatorApprovalChanged.AfterProperties.ExtendedProperties("txtComments").ToString&lt;br /&gt;&lt;br /&gt;13. go back to the design surface and double-click PeerReviewerApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;14. drag and drop a LogToHistoryListActivity activity below createPeerReviewerApprovalTask&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. name: hlogPeerReviewerApprovalTaskCreated&lt;br /&gt;    b. EventId: TaskCreated&lt;br /&gt;    c. HistoryDescription: Peer Reviewer approval task created. Awaiting approval.&lt;br /&gt;    d. UserId: 0&lt;br /&gt;&lt;br /&gt;15. go back to the design surface and double-click PeerReviewerApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;16. drag and drop a LogToHistoryListActivity activity between ifPeerReviewerApproved and setStateFinalState&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. name: hlogPeerReviewerApproved&lt;br /&gt;    b. EventId: TaskCompleted&lt;br /&gt;    c. HistoryDescription: Peer Reviewer approved.&lt;br /&gt;    d. HistoryOutcome: create property hlogPeerReviewerApproved_HistoryOutcome&lt;br /&gt;    e. UserId: create property hlogPeerReviewerApproved_UserId&lt;br /&gt;&lt;br /&gt;17. drag and drop a LogToHistoryListActivity activity between ifPeerReviewerRejected and setStateInitiatorApproval&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. name: hlogPeerReviewerRejected&lt;br /&gt;    b. EventId: TaskCompleted&lt;br /&gt;    c. HistoryDescription: Peer Reviewer rejected.&lt;br /&gt;    d. HistoryOutcome: create property hlogPeerReviewerRejected_HistoryOutcome&lt;br /&gt;    e. UserId: create property hlogPeerReviewerRejected_UserId&lt;br /&gt;&lt;br /&gt;18. click on the onPeerReviewerApprovalChanged activity and set the following property&lt;br /&gt;    a. Executor: create property onPeerReviewerApprovalChanged_Executor&lt;br /&gt;&lt;br /&gt;19. double-click onPeerReviewerApprovalChanged activity to be taken to its code stub&lt;br /&gt;&lt;br /&gt;20. copy and paste the following code to have the task's executor assigned to the UserId property of the&lt;br /&gt;    logToHistory activity:&lt;br /&gt;&lt;br /&gt;      'assign task's executor to the LogToHistory activity's user id&lt;br /&gt;      Dim executor As SPUser = GetUserObject(onPeerReviewerApprovalChanged_Executor)&lt;br /&gt;      hlogPeerReviewerApproved_UserId = executor.ID&lt;br /&gt;      hlogPeerReviewerRejected_UserId = executor.ID&lt;br /&gt;&lt;br /&gt;      'assign the task's comments to the HistoryOutcome property&lt;br /&gt;      hlogPeerReviewerApproved_HistoryOutcome = "Comments: " &amp; onPeerReviewerApprovalChanged.AfterProperties.ExtendedProperties("txtComments").ToString&lt;br /&gt;      hlogPeerReviewerRejected_HistoryOutcome = "Comments: " &amp; onPeerReviewerApprovalChanged.AfterProperties.ExtendedProperties("txtComments").ToString&lt;br /&gt;&lt;br /&gt;21. go back to the design surface and double-click PeerReviewerApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;22. drag and drop a LogToHistoryListActivity activity between hlogPeerReviewerApproved and setStateFinalState&lt;br /&gt;    and set the following properties:&lt;br /&gt;    a. name: hlogWorkflowCompleted&lt;br /&gt;    b. EventId: WorkflowCompleted&lt;br /&gt;    c. HistoryDescription: Workflow completed.&lt;br /&gt;    d. UserId: 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Posts in this series:&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html"&gt;Part 1: Introduction&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;Part 2: Create the initiation form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;Part 3: Create the task form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;Part 4: Create the state machine workflow&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;Part 5: Add workflow history logging&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;Part 6: Add task notification emails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-1545273020451354671?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/1545273020451354671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1545273020451354671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1545273020451354671'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html' title='How to create a SharePoint State Machine Workflow: Part 5 - Add workflow history logging'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-5791973334672244509</id><published>2009-08-19T22:01:00.014-04:00</published><updated>2011-07-29T15:21:24.221-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 4 - Create the state machine workflow</title><content type='html'>Here in Part 4, we create the workflow and deploy it to the SharePoint server. I have done these steps with both Visual Studio 2005 and 2008. When deploying, it tells you what to do based on whether you are using 2005 or 2008. Also, I included VB as well as C# code depending on your tastes. It seems like most everybody is using C#. We use VB in-house traditionally so I figured I would stay with VB. You'll notice that there really isn't a whole lot of code here at all - just lots and lots of steps. This workflow works off of four states: InitialState, InitiatorApproval,  PeerReviewerApproval and FinalState.&lt;br /&gt;&lt;pre style="font-family: ariel;"&gt;&lt;br /&gt;1. In Visual Studio 2005, File -&gt; New -&gt; Project -&gt; SharePoint -&gt; SharePoint Server State Machine, call it AgendaItemPeerReview&lt;br /&gt;In Visual Studio 2008, File -&gt; New -&gt; Project -&gt; Visual Basic -&gt; Office -&gt; 2007 -&gt; SharePoint 2007 State Machine Workflow, &lt;br /&gt;  call it AgendaItemPeerReview&lt;br /&gt;   set location to d:\asp or whatever you want&lt;br /&gt;   Rename Workflow1.vb to PeerReviewWorkflow.vb and doubleclick it to open the design surface.&lt;br /&gt;   It will start out with a state called Workflow1InitialState. Rename Workflow1InitialState to InitialState.&lt;br /&gt;   &lt;br /&gt;Note: The workflow MUST have a different name than the solution/project. Do NOT rename file workflow1.cs (and thus the&lt;br /&gt;      workflow1 class) to the same name you chose as the solution/project.&lt;br /&gt;&lt;br /&gt;2. add three more states and name them InitiatorApproval, PeerReviewerApproval and FinalState&lt;br /&gt;&lt;br /&gt;3. right click workflow and set CompletedStateFinish property to the new state you created called FinalState&lt;br /&gt;   be sure InitialStateName is InitialState&lt;br /&gt;&lt;br /&gt;We will build the workflow by adding activities to the various states.&lt;br /&gt;Afterwards, we will come back and add the necessary code. I do it this way because some code is autogenerated while&lt;br /&gt;  other code must be written by us, and by building the whole workflow first, all the generated code will be together,&lt;br /&gt;  then all our code will be together below the generated code. Also, this provides more of a top-down approach to&lt;br /&gt;  designing (and understanding) the workflow.&lt;br /&gt;&lt;br /&gt;4. in state InitialState, click eventDrivenActivity1 and rename it to InitialStateActivities&lt;br /&gt;&lt;br /&gt;5. double-click this activity to drill down into it; in here we will create a sequential workflow; notice there is&lt;br /&gt;   already an onWorkflowActivated1 event&lt;br /&gt;   a. Rename onWorkflowActivated1 to onWorkflowActivated.&lt;br /&gt;   b. Drill down into CorrelationToken and change OwnerActivityName to PeerReviewWorkflow.&lt;br /&gt;   c. Drill down into WorkflowProperties and change Name to PeerReviewWorkflow.&lt;br /&gt;&lt;br /&gt;6. in the Toolbox, find the Windows Workflow section and drag and drop a SetState event below onWorkflowActivated&lt;br /&gt;   a. rename setState activity to setStatePeerReviewerApproval&lt;br /&gt;   b. set TargetStateName property to PeerReviewerApproval&lt;br /&gt;   c. click on PeerReviewWorkflow to go back to workflow view; notice the line drawn from InitialState to&lt;br /&gt;      state PeerReviewerApproval&lt;br /&gt;&lt;br /&gt;For the two approval states, we are going to add the same stuff, ie initialization code for when a state is entered,&lt;br /&gt;   code to execute while in the state, and code to execute when the state is completed.&lt;br /&gt;&lt;br /&gt;7. for each of the two states, drag and drop into them:&lt;br /&gt;   a. a StateInitialization activity&lt;br /&gt;   b. an EventDriven activity&lt;br /&gt;   c. a StateFinalization activity&lt;br /&gt;&lt;br /&gt;8. in stateInitiatorApproval, rename the activities to:&lt;br /&gt;     InitiatorApprovalInitialization&lt;br /&gt;     InitiatorApprovalActivities&lt;br /&gt;     InitiatorApprovalFinalization&lt;br /&gt;&lt;br /&gt;9. in statePeerReviewerApproval, rename the activities to:&lt;br /&gt;     PeerReviewerApprovalInitialization&lt;br /&gt;     PeerReviewerApprovalActivities&lt;br /&gt;     PeerReviewerApprovalFinalization&lt;br /&gt;&lt;br /&gt;We will now add the specific tasks within each of these three activities (initialization, activities, finalization) for each state.&lt;br /&gt;Essentially, we will be creating sequential workflows within each of these activities.&lt;br /&gt;&lt;br /&gt;10. in InitiatorApproval, double-click InitiatorApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;11. drag and drop a CreateTask activity and set the following properties:&lt;br /&gt;    a. rename it createInitiatorApprovalTask&lt;br /&gt;    b. set CorrelationToken to InitiatorApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       InitiatorApproval&lt;br /&gt;    c. for TaskId, click the ellipses, click the "Bind to a new member" tab, remove the 1 from the end of the "New member name"&lt;br /&gt;       and click OK&lt;br /&gt;    d. follow the same step for TaskProperties&lt;br /&gt;    e. exit this sequential workflow view by clicking on PeerReviewWorkflow on the design surface&lt;br /&gt;&lt;br /&gt;12. double-click InitiatorApprovalFinalization to drill down into it&lt;br /&gt;&lt;br /&gt;13. drag and drop a CompleteTask activity and set the following properties:&lt;br /&gt;    a. rename it completeInitiatorApprovalTask&lt;br /&gt;    b. set CorrelationToken to InitiatorApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       InitiatorApproval&lt;br /&gt;    c. for TaskId, click the ellipses, on the "Bind to an existing member" tab, click createInitiatorApprovalTask_TaskId&lt;br /&gt;    d. exit this sequential workflow view by clicking on PeerReviewWorkflow on the design surface&lt;br /&gt;&lt;br /&gt;14. double-click InitiatorApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;15. drag and drop an OnTaskChanged activity and set the following properties:&lt;br /&gt;    a. rename it onInitiatorApprovalChanged&lt;br /&gt;    b. set CorrelationToken to InitiatorApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       InitiatorApproval&lt;br /&gt;    c. for TaskId, click the ellipses, on the "Bind to an existing member" tab, click createInitiatorApprovalTask_TaskId&lt;br /&gt;    d. for AfterProperties and BeforeProperties, click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;       remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;&lt;br /&gt;16. drag and drop an IfElse activity and rename it InitiatorApprovalStatus&lt;br /&gt;&lt;br /&gt;17. click on the left branch and set the following properties:&lt;br /&gt;    a. rename it ifInitiatorApproved&lt;br /&gt;    b. for Condition, click Code Condition&lt;br /&gt;&lt;br /&gt;18. drag and drop a setState activity under ifInitiatorApproved&lt;br /&gt;    a. rename it setStatePeerReviewerApproval&lt;br /&gt;    b. set the TargetStateName to PeerReviewerApproval&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;19. click PeerReviewWorkflow to go back to higher-level view of workflow&lt;br /&gt;&lt;br /&gt;20. in PeerReviewerApproval, double-click PeerReviewerApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;21. drag and drop a CreateTask activity and set the following properties:&lt;br /&gt;    a. rename it createPeerReviewerApprovalTask&lt;br /&gt;    b. set CorrelationToken to PeerReviewerApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       PeerReviewerApproval&lt;br /&gt;    c. for TaskId, click the ellipses, click the "Bind to a new member" tab, remove the 1 from the end of the "New member name"&lt;br /&gt;       and click OK&lt;br /&gt;    d. follow the same step for TaskProperties&lt;br /&gt;    e. exit this sequential workflow view by clicking on PeerReviewWorkflow on the design surface&lt;br /&gt;&lt;br /&gt;22. double-click PeerReviewerApprovalFinalization to drill down into it&lt;br /&gt;&lt;br /&gt;23. drag and drop a CompleteTask activity and set the following properties:&lt;br /&gt;    a. rename it completePeerReviewerApprovalTask&lt;br /&gt;    b. set CorrelationToken to PeerReviewerApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       PeerReviewerApproval&lt;br /&gt;    c. for TaskId, click the ellipses, on the "Bind to an existing member" tab, click createPeerReviewerApprovalTask_TaskId&lt;br /&gt;    d. exit this sequential workflow view by clicking on PeerReviewWorkflow on the design surface&lt;br /&gt;&lt;br /&gt;24. double-click PeerReviewerApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;25. drag and drop an OnTaskChanged activity and set the following properties:&lt;br /&gt;    a. rename it onPeerReviewerApprovalChanged&lt;br /&gt;    b. set CorrelationToken to PeerReviewerApprovalToken; click the + next to it to set the OwnerActivityName to&lt;br /&gt;       InitiatorApproval&lt;br /&gt;    c. for TaskId, click the ellipses, on the "Bind to an existing member" tab, click createPeerReviewerApprovalTask_TaskId&lt;br /&gt;    d. for AfterProperties and BeforeProperties, click the ellipses, click the "Bind to a new member" tab,&lt;br /&gt;       remove the 1 from the end of the "New member name" and click OK&lt;br /&gt;&lt;br /&gt;26. drag and drop an IfElse activity and rename it PeerReviewerApprovalStatus&lt;br /&gt;&lt;br /&gt;27. click on the left branch and set the following properties:&lt;br /&gt;    a. rename it ifPeerReviewerApproved&lt;br /&gt;    b. for Condition, click Code Condition&lt;br /&gt;&lt;br /&gt;28. drag and drop a setState activity under ifPeerReviewerApproved&lt;br /&gt;    a. rename it setStateFinalState&lt;br /&gt;    b. set the TargetStateName to FinalState&lt;br /&gt;&lt;br /&gt;29. click on the right branch and set the following properties:&lt;br /&gt;    a. rename it ifPeerReviewerRejected&lt;br /&gt;    b. for Condition, click Code Condition&lt;br /&gt;&lt;br /&gt;30. drag and drop a setState activity under ifPeerReviewerRejected&lt;br /&gt;    a. rename it setStateInitiatorApproval&lt;br /&gt;    b. set the TargetStateName to InitiatorApproval&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now we are ready to start adding code. First we will add code in InitialState, for when the workflow starts.&lt;br /&gt;&lt;br /&gt;31. Go to the code-behind and find the following line (this is the VB version, but the C# version looks almost identical):&lt;br /&gt;    Public workflowProperties As SPWorkflowActivationProperties = New Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties&lt;br /&gt;&lt;br /&gt;    Above it, type:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            Public workflowId As System.Guid&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            public Guid workflowId = default(System.Guid);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    Below the workflowProperties line, type:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            Dim PeerReviewer As String&lt;br /&gt;            Dim CC As String&lt;br /&gt;            Dim Instructions As String&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            private String PeerReviewer = default(String);&lt;br /&gt;            private String CC = default(String);&lt;br /&gt;            private String Instructions = default(String);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;32. back on design surface, double-click InitialStateActivitites to drill down into it&lt;br /&gt;&lt;br /&gt;33. double-click onWorkflowActivated. This will take you to the code behind and show the generated code stub&lt;br /&gt;    onWorkflowActivated_Invoked. Copy and paste the following code into it:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            workflowId = workflowProperties.WorkflowId&lt;br /&gt;&lt;br /&gt;            Dim serializer As XmlSerializer = New XmlSerializer(GetType(InitForm))&lt;br /&gt;            Dim reader As XmlTextReader = New XmlTextReader(New System.IO.StringReader(workflowProperties.InitiationData))&lt;br /&gt;            Dim initform As InitForm = CType(serializer.Deserialize(reader), InitForm)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            Dim prPeerReviewer() As Person&lt;br /&gt;            prPeerReviewer = initform.gpPeerReviewer&lt;br /&gt;            PeerReviewer = prPeerReviewer(0).AccountId&lt;br /&gt;&lt;br /&gt;            Dim prCC() As Person&lt;br /&gt;            prCC = initform.gpCC&lt;br /&gt;            CC = prCC(0).AccountId&lt;br /&gt; &lt;br /&gt;            Instructions = initform.txtInstructions&lt;br /&gt;&lt;br /&gt;        &lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            workflowId = workflowProperties.WorkflowId;&lt;br /&gt;&lt;br /&gt;            XmlSerializer serializer = new XmlSerializer(typeof(InitForm));&lt;br /&gt;            XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(workflowProperties.InitiationData));&lt;br /&gt;            InitForm initform = (InitForm)serializer.Deserialize(reader);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            Person prPeerReviewer = new Person();&lt;br /&gt;            prPeerReviewer = (Person)initform.gpPeerReviewer[0];&lt;br /&gt;            //PeerReviewer = prPeerReviewer.AccountId.GetValue(0).ToString();&lt;br /&gt;            PeerReviewer = prPeerReviewer.AccountId;&lt;br /&gt;&lt;br /&gt;            Person prCC = new Person();&lt;br /&gt;            prCC = (Person)initform.gpCC[0];&lt;br /&gt;            //CC = prCC.AccountId.GetValue(0).ToString();&lt;br /&gt;            CC = prCC.AccountId;&lt;br /&gt;&lt;br /&gt;            Instructions = initform.txtInstructions;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;34. go to the top of PeerReviewWorkflow.vb and add the following code:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            imports System.Xml.Serialization&lt;br /&gt;            imports System.Xml&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            using System.Xml.Serialization;&lt;br /&gt;            using System.Xml;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next, we will add code to the InitiatorApproval state.&lt;br /&gt;&lt;br /&gt;35. go back to the design surface and double-click InitiatorApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;36. double-click createInitiatorApprovalTask to be taken to its MethodInvoking code stub. Copy and paste&lt;br /&gt;    the following code in it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            createInitiatorApprovalTask_TaskId = Guid.NewGuid&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties = new SPWorkflowTaskProperties&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.Title = "Initiator Approval of " &amp; workflowProperties.Item.File.Name&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.AssignedTo = workflowProperties.Originator&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.Description = ""&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.TaskType = 0&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.ExtendedProperties("txtInstructions") = Instructions&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            createInitiatorApprovalTask_TaskId = Guid.NewGuid();&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties = new SPWorkflowTaskProperties();&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.Title = "Initiator Approval of " + workflowProperties.Item.File.Name;&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.AssignedTo = workflowProperties.Originator;&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.Description = "";&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.TaskType = 0;&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.ExtendedProperties["txtInstructions"] = Instructions;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;37. go back to the design surface and double-click InitiatorApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;38. double-click onInitiatorApprovalChanged to be taken to its Invoked code stub. Copy and paste&lt;br /&gt;    the following code in it.&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            onInitiatorApprovalChanged_AfterProperties = onInitiatorApprovalChanged.AfterProperties&lt;br /&gt;            onInitiatorApprovalChanged_BeforeProperties = onInitiatorApprovalChanged.BeforeProperties&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            onInitiatorApprovalChanged_AfterProperties = onInitiatorApprovalChanged.AfterProperties;&lt;br /&gt;            onInitiatorApprovalChanged_BeforeProperties = onInitiatorApprovalChanged.BeforeProperties;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;39. go back to the design surface and click ifInitiatorApproved and set the following properties:&lt;br /&gt;    a. drill down into the Condition property (already set to Code Condition), and next to the second&lt;br /&gt;       Condition property, type InitiatorApproved and hit enter&lt;br /&gt;    b. you will be taken to the code stub for InitiatorApproved routine; copy and paste the following&lt;br /&gt;       code into it:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            If onInitiatorApprovalChanged_AfterProperties.ExtendedProperties("status").ToString = "accepted" Then&lt;br /&gt;                e.Result = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            if (onInitiatorApprovalChanged_AfterProperties.ExtendedProperties["status"].ToString() == "accepted")&lt;br /&gt;            {&lt;br /&gt;                e.Result = true;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next, we will add code to the PeerReviewerApproval state.&lt;br /&gt;&lt;br /&gt;40. go back to the design surface and double-click PeerReviewerApprovalInitialization to drill down into it&lt;br /&gt;&lt;br /&gt;41. double-click createPeerReviewerApprovalTask to be taken to its MethodInvoking code stub. Copy and paste&lt;br /&gt;    the following code in it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            createPeerReviewerApprovalTask_TaskId = Guid.NewGuid&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties = new SPWorkflowTaskProperties&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.Title = "Peer Reviewer Approval of " &amp; workflowProperties.Item.File.Name&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.AssignedTo = PeerReviewer&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.Description = ""&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.TaskType = 0&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.ExtendedProperties("txtInstructions") = Instructions&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            createPeerReviewerApprovalTask_TaskId = Guid.NewGuid();&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties = new SPWorkflowTaskProperties();&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.Title = "Peer Reviewer Approval of " + workflowProperties.Item.File.Name;&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.AssignedTo = PeerReviewer;&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.Description = "";&lt;br /&gt;            createPeerReviewerApprovalTask_TaskProperties.TaskType = 0;&lt;br /&gt;            createInitiatorApprovalTask_TaskProperties.ExtendedProperties["txtInstructions"] = Instructions;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;42. go back to the design surface and double-click PeerReviewerApprovalActivities to drill down into it&lt;br /&gt;&lt;br /&gt;43. double-click onPeerReviewerApprovalChanged to be taken to its Invoked code stub. Copy and paste&lt;br /&gt;    the following code in it.&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            onPeerReviewerApprovalChanged_AfterProperties = onPeerReviewerApprovalChanged.AfterProperties&lt;br /&gt;            onPeerReviewerApprovalChanged_BeforeProperties = onPeerReviewerApprovalChanged.BeforeProperties&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            onPeerReviewerApprovalChanged_AfterProperties = onPeerReviewerApprovalChanged.AfterProperties;&lt;br /&gt;            onPeerReviewerApprovalChanged_BeforeProperties = onPeerReviewerApprovalChanged.BeforeProperties;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;44. go back to the design surface and click ifPeerReviewerApproved and set the following properties:&lt;br /&gt;    a. drill down into the Condition property (already set to Code Condition), and next to the second&lt;br /&gt;       Condition property, type PeerReviewerApproved and hit enter&lt;br /&gt;    b. you will be taken to the code stub for PeerReviewerApproved routine; copy and paste the following&lt;br /&gt;       code into it:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            If onPeerReviewerApprovalChanged_AfterProperties.ExtendedProperties("status").ToString = "accepted" Then&lt;br /&gt;                e.Result = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            if (onPeerReviewerApprovalChanged_AfterProperties.ExtendedProperties["status"].ToString() == "accepted")&lt;br /&gt;            {&lt;br /&gt;                e.Result = true;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;45. go back to the design surface and click ifPeerReviewerRejected and set the following properties:&lt;br /&gt;    a. drill down into the Condition property (already set to Code Condition), and next to the second&lt;br /&gt;       Condition property, type PeerReviewerRejected and hit enter&lt;br /&gt;    b. you will be taken to the code stub for PeerReviewerApproved routine; copy and paste the following&lt;br /&gt;       code into it:&lt;br /&gt;&lt;br /&gt;        VISUAL BASIC&lt;br /&gt;        ------------&lt;br /&gt;            If onPeerReviewerApprovalChanged_AfterProperties.ExtendedProperties("status").ToString = "rejected" Then&lt;br /&gt;                e.Result = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        VISUAL C#&lt;br /&gt;        ---------&lt;br /&gt;            if (onPeerReviewerApprovalChanged_AfterProperties.ExtendedProperties["status"].ToString() == "rejected")&lt;br /&gt;            {&lt;br /&gt;                e.Result = true;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;46. sign the assembly&lt;br /&gt;    a. right-click the project, then click properties&lt;br /&gt;    b. click Signing&lt;br /&gt;    c. if Sign the assembly is not already checked...&lt;br /&gt;    d. check Sign the assembly&lt;br /&gt;    e. choose &amp;lt;new&amp;gt; from the dropdown&lt;br /&gt;    f. enter MyKeyFile for the key file name&lt;br /&gt;    g. uncheck Protect my key file with a password&lt;br /&gt;    h. click OK&lt;br /&gt;&lt;br /&gt;47. follow instructions above for creating the workflow's Initiation and Task forms using Microsoft Office InfoPath&lt;br /&gt;&lt;br /&gt;48. If developing in Visual Studio 2005: copy PeerReviewInit.xsn and PeerReviewTask.xsn to DeploymentFiles\FeatureFiles folder&lt;br /&gt;&lt;br /&gt;49. be sure feature.xml is set up correctly&lt;br /&gt;    a. title: Agenda Item Peer Review feature&lt;br /&gt;    b. description: This feature installs the Agenda Item Peer Review workflow&lt;br /&gt;    c. if deploying via Visual Studio 2008:&lt;br /&gt;       under the &amp;lt;ElementManifests&amp;gt; tag, add:&lt;br /&gt;       &amp;lt;ElementFile Location="PeerReviewInit.xsn" /&amp;gt;&lt;br /&gt;       &amp;lt;ElementFile Location="PeerReviewTask.xsn" /&amp;gt;&lt;br /&gt;&lt;br /&gt;50. be sure workflow.xml is set up correctly&lt;br /&gt;    a. name: AgendaItemPeerReview&lt;br /&gt;    b. description: This is the Agenda Item Peer Review workflow&lt;br /&gt;    c. CodeBesideClass: AgendaItemPeerReview.PeerReviewWorkflow&lt;br /&gt;    d. after CodeBesideAssembly, you must have the following:&lt;br /&gt;       TaskListContentTypeId="0x01080100C9C9515DE4E24001905074F980F93160"&lt;br /&gt;    e. after TaskListContentTypeId, you must have the following:&lt;br /&gt;       AssociationUrl="_layouts/CstWrkflIP.aspx"&lt;br /&gt;       InstantiationUrl="_layouts/IniWrkflIP.aspx"&lt;br /&gt;       ModificationUrl="_layouts/ModWrkflIP.aspx"&lt;br /&gt;    f. copy initiation form's urn into the &amp;lt;Association_FormURN&amp;gt; and &amp;lt;Instantiation_FormURN&amp;gt; tags&lt;br /&gt;    g. copy task form's urn into the &amp;lt;Task0_FormURN&amp;gt; tag&lt;br /&gt;&lt;br /&gt;51. deploy solution&lt;br /&gt;    a. Visual Studio 2005: right click project, click Properties, click Build Events, change NODEPLOY to&lt;br /&gt;       DEPLOY at the end of the Post-build event command line, click Build, click Rebuild Solution&lt;br /&gt;    b. Visual Studio 2008: click Build, then click Deploy Solution&lt;br /&gt;&lt;br /&gt;52. The first time you deploy it, the AgendaItemPeerReview assembly is added to the GAC. The workflow.xml&lt;br /&gt;    file needs the assembly's public key token.&lt;br /&gt;    a. Start, Settings, Control Panel, Administrative Tools, Microsoft .NET Framework 2.0 Configuration&lt;br /&gt;    b. click Manage the Assembly Cache&lt;br /&gt;    c. click View List of Assemblies in the Assembly Cache&lt;br /&gt;    d. right-click on AgendaItemPeerReview and click Properties&lt;br /&gt;    e. copy the public key token and click OK&lt;br /&gt;    f. paste the public key token in workflow.xml where you find PublicKeyToken&lt;br /&gt;    g. redeploy solution (step 51)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Another method for retrieving the assembly's public key token&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;If you want to skip step 52, retrieve the public key token using the sn.exe utility then&lt;br /&gt;  update workflow.xml (step 50) with it&lt;br /&gt;&lt;br /&gt;1. copy sn.exe from c:\program files\microsoft visual studio 8\sdk\v2.0\bin to d:\&lt;br /&gt;2. change directory to d:\asp\agendaitempeerreview\agendaitempeerreview\bin\debug&lt;br /&gt;3. d:\sn -T agendaitempeerreview.dll&lt;br /&gt;4. copy (or type) the resulting public key token into the workflow.xml&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Posts in this series:&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html"&gt;Part 1: Introduction&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;Part 2: Create the initiation form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;Part 3: Create the task form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;Part 4: Create the state machine workflow&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;Part 5: Add workflow history logging&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;Part 6: Add task notification emails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-5791973334672244509?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/5791973334672244509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/5791973334672244509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/5791973334672244509'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html' title='How to create a SharePoint State Machine Workflow: Part 4 - Create the state machine workflow'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-6495686145201265850</id><published>2009-08-19T21:48:00.007-04:00</published><updated>2009-08-20T23:06:02.266-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='infopath'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='task form'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 3 - Create the task form</title><content type='html'>In Part 3, we will create the workflow's task form. In Part 4, we will configure this form to be called as the task form.&lt;br /&gt;&lt;pre style="font-family: ariel;"&gt;&lt;br /&gt;1. load Microsoft Office InfoPath 2007&lt;br /&gt;2. Under Design a form, click Design a Form Template, click Blank, then click OK&lt;br /&gt;3. click Insert, Layout Table..., 2 columns, 3 rows, click OK&lt;br /&gt;4. add three buttons to the right column, last row&lt;br /&gt;5. in Design Tasks, click Data Source&lt;br /&gt;    a. double-click myFields and rename it to TaskForm and click OK&lt;br /&gt;    b. right-click TaskForm and click Add...&lt;br /&gt;    c. for name, type status&lt;br /&gt;    d. click OK&lt;br /&gt;6. double-click the first button&lt;br /&gt;   a. change Label to Approve&lt;br /&gt;   b. click Rules...&lt;br /&gt;   c. click Add...&lt;br /&gt;   d. click Add Action...&lt;br /&gt;   e. select Submit using a data connection from dropdown&lt;br /&gt;   f. click Add...&lt;br /&gt;   g. be sure Create a new connection to submit data is selected and click Next&lt;br /&gt;   h. select To the hosting environment, such as an ASP.NET page or a hosting application&lt;br /&gt;   i. leave Submit as the name for the data connection and click Finish&lt;br /&gt;   j. click OK&lt;br /&gt;   k. click Add Action...&lt;br /&gt;   l. select Set a field's value&lt;br /&gt;   m. click icon to the right of the Field textbox&lt;br /&gt;   n. click status and click OK&lt;br /&gt;   o. for value, type: accepted&lt;br /&gt;   p. click OK&lt;br /&gt;   q. click Add Action...&lt;br /&gt;   r. select Close the form from the dropdown&lt;br /&gt;   s. uncheck If changes have not been saved...&lt;br /&gt;   t. click OK&lt;br /&gt;   u. click OK&lt;br /&gt;   v. click OK&lt;br /&gt;   w. click OK&lt;br /&gt;7. double-click the second button&lt;br /&gt;   a. change Label to Reject&lt;br /&gt;   b. click Rules...&lt;br /&gt;   c. click Add...&lt;br /&gt;   d. click Add Action...&lt;br /&gt;   e. select Submit using a data connection from dropdown&lt;br /&gt;   f. click Add...&lt;br /&gt;   g. be sure Create a new connection to submit data is selected and click Next&lt;br /&gt;   h. select To the hosting environment, such as an ASP.NET page or a hosting application&lt;br /&gt;   i. leave Submit as the name for the data connection and click Finish&lt;br /&gt;   j. click OK&lt;br /&gt;   k. click Add Action...&lt;br /&gt;   l. select Set a field's value&lt;br /&gt;   m. click icon to the right of the Field textbox&lt;br /&gt;   n. click status and click OK&lt;br /&gt;   o. for value, type: rejected&lt;br /&gt;   p. click OK&lt;br /&gt;   q. click Add Action...&lt;br /&gt;   r. select Close the form from the dropdown&lt;br /&gt;   s. uncheck If changes have not been saved...&lt;br /&gt;   t. click OK&lt;br /&gt;   u. click OK&lt;br /&gt;   v. click OK&lt;br /&gt;   w. click OK&lt;br /&gt;8. double-click the third button&lt;br /&gt;   a. change Label to Cancel&lt;br /&gt;   b. click Rules...&lt;br /&gt;   c. click Add...&lt;br /&gt;   d. click Add Action...&lt;br /&gt;   e. select Close the form from the dropdown&lt;br /&gt;   f. uncheck If changes have not been saved...&lt;br /&gt;   g. click OK&lt;br /&gt;   h. click OK&lt;br /&gt;   i. click OK&lt;br /&gt;   j. click OK&lt;br /&gt;9. in left column, first row, type: Instructions&lt;br /&gt;10. in left column, second row, type: Comments&lt;br /&gt;11. in the right column, first row, drop a text box control into it&lt;br /&gt;    a. double-click the text box control&lt;br /&gt;    b. change field name to txtInstructions&lt;br /&gt;    c. click Display tab and check Multi-line&lt;br /&gt;    d. click OK&lt;br /&gt;    e. drag the bottom edge of the control down a little ways so a few lines of text will show&lt;br /&gt;12. in the right column, second row, drop a text box control into it&lt;br /&gt;    a. double-click the text box control&lt;br /&gt;    b. change field name to txtComments&lt;br /&gt;    c. click Display tab and check Multi-line&lt;br /&gt;    d. click OK&lt;br /&gt;    e. drag the bottom edge of the control down a little ways so a few lines of text will show&lt;br /&gt;13. when the task form loads, we want txtInstructions to populate automatically with what was typed in&lt;br /&gt;    to txtInstructions on the initiation form&lt;br /&gt;    a. on the Desktop, create a file called ItemMetadata.xml (CASE IS EXTREMELY IMPORTANT!!!) with the following text in it:&lt;br /&gt;       &amp;lt;z:row xmlns:z="#RowsetSchema" ows_txtInstructions="" /&amp;gt;&lt;br /&gt;    b. in Design Tasks, click Data Source, click Manage Data Connections... (near bottom)&lt;br /&gt;    c. click Add&lt;br /&gt;    d. click Create a connection to Receive data&lt;br /&gt;    e. click Next&lt;br /&gt;    f. be sure XML document is selected and click Next&lt;br /&gt;    g. click Browse and select ItemMetadata.xml from Desktop&lt;br /&gt;    h. click Next&lt;br /&gt;    i. be sure Include the data as a resource file... is selected and click Next&lt;br /&gt;    j. click Finish&lt;br /&gt;    k. click Close&lt;br /&gt;    l. double-click txtInstructions&lt;br /&gt;    m. click the fx button next to the value text field&lt;br /&gt;    n. click Insert Field or Group...&lt;br /&gt;    o. change data source in drop down to ItemMetadata (Secondary)&lt;br /&gt;    p. click :ows_txtInstructions&lt;br /&gt;    q. click Ok&lt;br /&gt;    r. click Ok&lt;br /&gt;    s. click Ok&lt;br /&gt;    Note: In the code that creates the tasks in the workflow, we will add txtInstructions to the task's&lt;br /&gt;          ExtendedProperties and populate it with txtInstructions from the initiation form. ItemMetaData.xml&lt;br /&gt;          will get this value from the task's txtInstructions property (added in ExtendedProperties) and will&lt;br /&gt;          then populate the task form's txtInstructions textbox on load.&lt;br /&gt;14. make form be able to open in a browser or in a client application&lt;br /&gt;    a. in Design Tasks, click Design Checker, click Change Compatibility Settings...&lt;br /&gt;    b. in the Compatibility category, check Design a form template that can be opened in a browser or InfoPath&lt;br /&gt;    c. in the Security and Trust category, uncheck Automatically determine security level, click Full Trust&lt;br /&gt;    d. click OK&lt;br /&gt;15. save form in My Documents as PeerReviewTask.xsn&lt;br /&gt;16. retrieve the form's id&lt;br /&gt;    a. click File, Properties&lt;br /&gt;    b. copy text in ID textbox into notepad to be used in a later step. it should look like this:&lt;br /&gt;       urn:schemas-microsoft-com:office:infopath:PeerReviewTask:-myXSD-2008-07-30T18-33-30&lt;br /&gt;17. publish the InfoPath form&lt;br /&gt;    a. click File, Publish&lt;br /&gt;    b. select To a network location&lt;br /&gt;    c. click Next&lt;br /&gt;    d. browse to the workflow project's location and name the file PeerReviewTask.xsn&lt;br /&gt;    e. click OK&lt;br /&gt;    f. click Next&lt;br /&gt;    g. delete the path in the textbox and click Next&lt;br /&gt;    h. click Publish&lt;br /&gt;    i. click Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Posts in this series:&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html"&gt;Part 1: Introduction&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;Part 2: Create the initiation form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;Part 3: Create the task form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;Part 4: Create the state machine workflow&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;Part 5: Add workflow history logging&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;Part 6: Add task notification emails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-6495686145201265850?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/6495686145201265850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6495686145201265850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/6495686145201265850'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html' title='How to create a SharePoint State Machine Workflow: Part 3 - Create the task form'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8858011036435013038</id><published>2009-08-19T21:15:00.012-04:00</published><updated>2009-08-20T23:06:15.901-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='infopath'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='initiation form'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 2 - Create the initiation form</title><content type='html'>In Part 2, we will create the workflow's initiation form. In Part 4, we will configure this form to be called in two locations: first, when a user associates the workflow with a document library, and second, when a user instantiates the workflow on a particular document. Thus, we are using the same form as both the association form and the initiation form. Its possible to use different forms collecting different information, but for simplicity, we will use the same form for both.&lt;br /&gt;&lt;pre style="font-family: ariel;"&gt;&lt;br /&gt;1. load Microsoft Office InfoPath 2007&lt;br /&gt;2. Under Design a form, click Design a Form Template, click Blank, then click OK&lt;br /&gt;3. if the Contact Selector control has not been added to the controls list, you will need to add it&lt;br /&gt;   a. on the Design Tasks pane, click Controls; if Contact Selector does not appear&lt;br /&gt;      under the Custom controls in the bottom of the list, continue with the following&lt;br /&gt;      steps. If it does appear there (ie you already added it), skip to step 4&lt;br /&gt;   b. click Add or Remove Custom Control...&lt;br /&gt;   c. click Add button&lt;br /&gt;   d. click ActiveX Control and click Next&lt;br /&gt;   e. click Contact Selector from the list and click Next&lt;br /&gt;   f. click Don't include a .cab file and click Next&lt;br /&gt;   g. click Value and click Next&lt;br /&gt;   h. in the Field or group type dropdown, select Field or Group (any data type) and click Finish, Close, OK&lt;br /&gt;4. click Insert, Layout Table..., 2 columns, 4 rows, click OK&lt;br /&gt;5. add two buttons to the right column, last row&lt;br /&gt;6. double-click the first button&lt;br /&gt;   a. change Label to OK&lt;br /&gt;   b. click Rules...&lt;br /&gt;   c. click Add...&lt;br /&gt;   d. click Add Action...&lt;br /&gt;   e. select Submit using a data connection from dropdown&lt;br /&gt;   f. click Add...&lt;br /&gt;   g. be sure Create a new connection to submit data is selected and click Next&lt;br /&gt;   h. select To the hosting environment, such as an ASP.NET page or a hosting application&lt;br /&gt;   i. leave Submit as the name for the data connection and click Finish&lt;br /&gt;   j. click OK&lt;br /&gt;   k. click Add Action...&lt;br /&gt;   l. select Close the form from the dropdown&lt;br /&gt;   m. uncheck If changes have not been saved...&lt;br /&gt;   n. click OK&lt;br /&gt;   o. click OK&lt;br /&gt;   p. click OK&lt;br /&gt;   q. click OK&lt;br /&gt;   r. stretch out OK button&lt;br /&gt;7. double-click the second button&lt;br /&gt;   a. change Label to Cancel&lt;br /&gt;   b. click Rules...&lt;br /&gt;   c. click Add...&lt;br /&gt;   d. click Add Action...&lt;br /&gt;   e. select Close the form from the dropdown&lt;br /&gt;   f. uncheck If changes have not been saved...&lt;br /&gt;   g. click OK&lt;br /&gt;   h. click OK&lt;br /&gt;   i. click OK&lt;br /&gt;   j. click OK&lt;br /&gt;8. in left column, first row, type: Peer Reviewer&lt;br /&gt;9. in left column, second row, type: CC&lt;br /&gt;10. in left column, third row, type: Instructions&lt;br /&gt;11. in the right column, third row, drop a text box control into it&lt;br /&gt;    a. double-click the text box control&lt;br /&gt;    b. change field name to txtInstructions&lt;br /&gt;    c. click Display tab and check Multi-line&lt;br /&gt;    d. click OK&lt;br /&gt;12. resize left column to be smaller than right column, resize txtInstructions to allow&lt;br /&gt;    for multiple lines&lt;br /&gt;13. in the right column, first row, drop a Contact Selector control into it&lt;br /&gt;    a. double-click the control, rename it to gpPeerReviewer, click OK&lt;br /&gt;14. in the right column, second row, drop a Contact Selector control into it&lt;br /&gt;    a. double-click the control, rename it to gpCC, click OK&lt;br /&gt;15. in Design Tasks, click Data Source&lt;br /&gt;    a. double-click myFields and rename it to InitForm and click OK&lt;br /&gt;    b. right click group1 and delete it&lt;br /&gt;    c. right click gpPeerReviewer and click Add&lt;br /&gt;    d. name is Person, the type is Group, check Repeating&lt;br /&gt;    e. right click Person, click Add..., enter name as DisplayName, click OK&lt;br /&gt;    f. right click Person, click Add..., enter name as AccountId, click OK&lt;br /&gt;    g. right click Person, click Add..., enter name as AccountType, click OK&lt;br /&gt;    h. right click Person, click Reference..., click gpCC, click OK&lt;br /&gt;16. make form be able to open in a browser or in a client application&lt;br /&gt;    a. in Design Tasks, click Design Checker, click Change Compatibility Settings...&lt;br /&gt;    b. in the Compatibility category, check Design a form template that can be opened in a browser or InfoPath&lt;br /&gt;    c. in the Security and Trust category, uncheck Automatically determine security level, click Full Trust&lt;br /&gt;    d. click OK&lt;br /&gt;17. create the context for the form so it knows where to retrieve people's names from:&lt;br /&gt;    a. open Notepad and type in the following and save it as Context.xml in the same folder as the workflow:&lt;br /&gt;       &amp;lt;Context&lt;br /&gt;        isStartWorkflow="true"&lt;br /&gt;        isRunAtServer="true"&lt;br /&gt;        provideAllFields="true"&lt;br /&gt;        siteUrl="http://sharepoint2007:100"&lt;br /&gt;       /&amp;gt;&lt;br /&gt;    Note: be sure siteUrl is for your site!!!&lt;br /&gt;18. add a secondary data source using Context.xml so form can retrieve contact data:&lt;br /&gt;    a. in Design Tasks, Data Source, click Manage Data Connections...&lt;br /&gt;    b. click Add... &lt;br /&gt;    c. Create a new connection to Receive Data&lt;br /&gt;    d. click Next&lt;br /&gt;    e. click XML document&lt;br /&gt;    f. click Next&lt;br /&gt;    g. browse to Context.xml&lt;br /&gt;    h. click Next&lt;br /&gt;    i. click Include the data as a resource file in the form template or template part&lt;br /&gt;    j. click Next&lt;br /&gt;    k. leave Context as the name of the data connection&lt;br /&gt;    l. ensure Automatically retrieve data when form is opened is checked&lt;br /&gt;    m. click Finish&lt;br /&gt;    n. click Close&lt;br /&gt;19. save form in My Documents as PeerReviewInit.xsn&lt;br /&gt;20. retrieve the form's id&lt;br /&gt;    a. click File, Properties&lt;br /&gt;    b. copy text in ID textbox into notepad to be used in a later step. it should look like this:&lt;br /&gt;       urn:schemas-microsoft-com:office:infopath:PeerReviewInit:-myXSD-2008-07-23T13-50-07&lt;br /&gt;21. publish the InfoPath form&lt;br /&gt;    a. click File, Publish&lt;br /&gt;    b. select To a network location&lt;br /&gt;    c. click Next&lt;br /&gt;    d. browse to the workflow project's location and name the file PeerReviewInit.xsn&lt;br /&gt;    e. click OK&lt;br /&gt;    f. click Next&lt;br /&gt;    g. delete the path in the textbox and click Next&lt;br /&gt;    h. click Publish&lt;br /&gt;    i. click Close&lt;br /&gt;22. create the schema for the form so the workflow can reference the contents of the form&lt;br /&gt;    a. click File, Save as source files...&lt;br /&gt;    b. browse to the folder where the workflow is&lt;br /&gt;    c. click OK&lt;br /&gt;    d. close Microsoft InfoPath (you will not be able to make any changes to the form once InfoPath is closed)&lt;br /&gt;    e. click Start, Run, type CMD and click OK&lt;br /&gt;    f. change folder to the workflow's folder&lt;br /&gt;    g. type:   xsd myschema.xsd /c /language:VB     (Note: you may have to find xsd.exe on your HD first)&lt;br /&gt;    h. rename the resulting myschema.vb to InitForm.vb&lt;br /&gt;23. add file to workflow project&lt;br /&gt;    a. in Visual Studio in Solution Explorer, Show All Files&lt;br /&gt;    b. right-click InitForm.vb and click Include In Project&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Posts in this series:&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html"&gt;Part 1: Introduction&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;Part 2: Create the initiation form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;Part 3: Create the task form&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;Part 4: Create the state machine workflow&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;Part 5: Add workflow history logging&lt;/a&gt;&lt;br /&gt;&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;Part 6: Add task notification emails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8858011036435013038?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8858011036435013038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8858011036435013038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8858011036435013038'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html' title='How to create a SharePoint State Machine Workflow: Part 2 - Create the initiation form'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-1739102285508311119</id><published>2009-08-19T20:35:00.011-04:00</published><updated>2009-08-21T00:00:23.092-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='state machine workflow'/><category scheme='http://www.blogger.com/atom/ns#' term='workflow'/><title type='text'>How to create a SharePoint State Machine Workflow: Part 1 - Introduction</title><content type='html'>A while back I was tasked with creating a SharePoint workflow that would enable peer review of a document. (BTW, this whole article series will be all about creating workflows with Visual Studio, not SharePoint Designer.) An initiator would assign the workflow to a document in a document library and set who they wanted to peer review it. Now, if you've searched the internet for SharePoint workflows, you've undoubtedly seen that one sequential workflow that always pops up in examples. The problem is that sometimes a sequential workflow doesn't cut it - you just might need a state machine workflow. Imagine having a workflow where someone wants to send a document out for approval to three people sequentially, like a manager, a division director, and a department director. After the manager approves it, it goes to the division director, then when that person approves it, the department director gets it. But what if the division director rejects it? You might want it to bounce back to the manager. Similarly, if the department director rejects it, it goes back to the division director. The state machine workflow is the way to do this as each person will be a state.&lt;br /&gt;&lt;br /&gt;This will be a six part series on how to create a SharePoint state machine workflow, although only the first four parts are necessary to create a functioning workflow&lt;br /&gt;. &lt;br /&gt;This example will employ a simple peer review model. An initiator will start a workflow on a document in a document library, assigning the person they want to act as the peer reviewer in the Initiation form. The workflow then starts. The peer reviewer will get a task. If the peer reviewer approves the task, the workflow is complete. If the peer review rejects the task, the initiator will get a task. In theory, the initiator could make changes to their document, and once they are done, they would approve the task, thus moving the flow back to the peer reviewer again. Once again, the peer reviewer would need to accept the task (which ends the workflow) or reject it (which sends the flow back to the initiator).&lt;br /&gt;&lt;br /&gt;You are currently reading Part 1 of this series.&lt;br /&gt;Part 2 will focus on using InfoPath to &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_19.html"&gt;create the workflow's Initiation form&lt;/a&gt;.&lt;br /&gt;Part 3 will focus on using InfoPath to &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_1265.html"&gt;create the workflow's Task form&lt;/a&gt;.&lt;br /&gt;Part 4 will focus on &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_5795.html"&gt;creating the state machine workflow&lt;/a&gt; itself.&lt;br /&gt;&lt;br /&gt;After part 4, you will have a fully functioning workflow. The following two parts are more like enhancements, but you'll likely want them.&lt;br /&gt;&lt;br /&gt;Part 5 will focus on &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_20.html"&gt;adding workflow history logging&lt;/a&gt; to the workflow.&lt;br /&gt;Part 6 will focus on &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine_2183.html"&gt;adding task notification emails&lt;/a&gt; to the workflow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-1739102285508311119?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/1739102285508311119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1739102285508311119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1739102285508311119'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/08/how-to-create-sharepoint-state-machine.html' title='How to create a SharePoint State Machine Workflow: Part 1 - Introduction'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-1976245410104156626</id><published>2009-07-23T21:00:00.043-04:00</published><updated>2009-07-25T10:10:52.537-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deployment'/><category scheme='http://www.blogger.com/atom/ns#' term='web part'/><category scheme='http://www.blogger.com/atom/ns#' term='feature'/><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='WSPBuilder'/><title type='text'>How to deploy a SharePoint web part to bin</title><content type='html'>As I was learning how to deploy my web parts as features in a solution package .wsp (I used WSPBuilder to create the .wsp), I realized I needed to make sure they were deployed not to the GAC (which is the default), but rather to the web application's /bin folder (under the web application in IIS). Bin is preferred over GAC because of security reasons: deploying to GAC unnecessarily exposes the web part's assembly .dll to every application on the server.&lt;br /&gt;&lt;br /&gt;This post will show you how to deploy a web part to the /bin folder (using WSPBuilder) AND how to configure it using CAS (Code Access Security) so your web part can actually run from that folder too! I kept finding articles that discuss one or the other, but not both. Those articles that did help me quite a bit have been referenced in the last part of this post.&lt;br /&gt;&lt;br /&gt;So, from the top, when you create a web part in Visual Studio 2008 and hit F5, the web part will deploy to the site you selected (right-click Project's Properties, click Debug, then whatever site you typed in Start browser with URL). In your Solution Explorer, you will notice a folder called /bin with another folder called /debug under that, and the compiled code will sit under there. This means its being deployed into the GAC.&lt;br /&gt;&lt;br /&gt;At a high level, there's really two steps we must do to get our web part to run from the web application's (your SharePoint site's) /bin folder. (You can get to /bin either by going into IIS and choosing the web application you want it deployed to, or by going to c:\inetpub\wwwroot\wss\VirtualDirectories\&amp;lt;web application&amp;gt;\bin.)&lt;br /&gt;&lt;br /&gt;The first step is actually deploying to the above-mentioned location. Once this is done, sure, the web part is right where you want it to be, but it won't run until we do step two, which involves setting the proper permissions to allow it to run from that spot. This second step involves modifying the web.config to look at a custom policy file where we will make changes to the CAS policies.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;First, deploy the web part assembly to the /bin:&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;1. In Solution Explorer, delete the /bin folder. It will reappear in a few seconds with a Debug subfolder, but it will be empty other than that.&lt;br /&gt;&lt;br /&gt;2. In Solution Explorer, create a folder called 80, and under that, create a folder called bin. So you will have /80/bin.&lt;br /&gt;&lt;br /&gt;3. Right-click the project in Solution Explorer, and on the Compile tab, change Build Output Path from bin\Debug\ to 80\bin\.&lt;br /&gt;&lt;br /&gt;4. In Solution Explorer, in AssemblyInfo.vb, you must add two lines:&lt;br /&gt;   a. below the other Imports statements, add the following: &lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;Imports System.Security&lt;/div&gt;  (Note: this will be a Using statement if you are doing C#)&lt;br /&gt;&lt;br /&gt;   b. Below one of the other Assembly statements, add the following: &lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;&amp;lt;assembly: AllowPartiallyTrustedCallers()&amp;gt;&lt;/div&gt;  (Note: This step doesn't affect the deployment of the web part, but comes into play when you try to actually use the web part on a page after its been deployed to /bin.)&lt;br /&gt;&lt;br /&gt;5. Now, when you compile the application you will notice the assembly (the dll) appears in /80/bin instead of where it was being put before, /bin/debug. If you hit F5 to deploy it, you will notice all the other deployment files Visual Studio creates is also placed in this folder. This leads me to my next point.&lt;br /&gt;&lt;br /&gt;Visual Studio creating deployment files automatically is no good. Sure, it will get everything up and running on the server, but maybe not the way you want. For instance, even though I have created a /12/TEMPLATE/FEATURES/AddContactFromAD folder structure in my Solution Explorer, and put in my feature.xml, elements.xml (mine is actually called AddContactFromAD.xml), and a web part definitions file AddContactFromAD.webpart, Visual Studio generates its own feature.xml (which doesn't have my pretty description I put in the real feature.xml). It also tends to write its own manifest.xml, wanting to deploy to GAC! So, let's deploy a much better way, using WSPBuilder! You can &lt;a href="http://www.codeplex.com/wspbuilder"&gt;download WSPBuilder here&lt;/a&gt;. Once installed, it will be added to Visual Studio's Tools menu.&lt;br /&gt;&lt;br /&gt;6. Assuming you have the above-mentioned 12 hive folder structure with a folder for each web part under FEATURES, and with that, a feature.xml, elements.xml, and .webpart file under each web part's folder, you can simply click Tools -&gt; WSPBuilder -&gt; Build WSP. WSP Builder will create the resulting .wsp and drop it in your project folder. Renaming it to .cab and opening it you will see each web part's feature.xml, elements.xml, .webpart, plus the assembly .dll and an autogenerated manifest.xml. Looking inside the manifest.xml will reveal two things: 1. the DeploymentTarget is WebApplication (no longer GlobalAssemblyCache as it was before), and 2. CAS policy permissions were added.&lt;br /&gt;&lt;br /&gt;7. Ok, so let's deploy this bad boy. To do this, I add the solution, deploy the solution, then activate the features (1 feature per web part). Below is my own batch file add.bat:&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;stsadm -o addsolution -filename contactuswebpart.wsp&lt;br /&gt;&lt;br /&gt;stsadm -o deploysolution -name contactuswebpart.wsp -url http://sharepoint2007:1001/ -immediate -allowCasPolicies&lt;br /&gt;&lt;br /&gt;stsadm -o activatefeature -name contactlist -url http://sharepoint2007:1001&lt;br /&gt;&lt;br /&gt;stsadm -o activatefeature -name emailcontact -url http://sharepoint2007:1001&lt;br /&gt;&lt;br /&gt;stsadm -o activatefeature -name addcontactfromad -url http://sharepoint2007:1001&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Also, my remove.bat comes in handy when testing:&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;stsadm -o deactivatefeature -name contactlist -url http://sharepoint2007:1001&lt;br /&gt;&lt;br /&gt;stsadm -o deactivatefeature -name emailcontact -url http://sharepoint2007:1001&lt;br /&gt;&lt;br /&gt;stsadm -o deactivatefeature -name addcontactfromad -url http://sharepoint2007:1001&lt;br /&gt;&lt;br /&gt;stsadm -o retractsolution -name contactuswebpart.wsp -url http://sharepoint2007:1001/ -immediate&lt;br /&gt;&lt;br /&gt;stsadm -o deletesolution -name contactuswebpart.wsp&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;At this point, if you check your web application's /bin, you should see the assembly .dll there. If you go to the Web Part Gallery and click the web part, or if you attempt to add the web part to a page, you will get an error. This is because we still need to do Part 2 of this grand adventure: modify the permissions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;For the second and final step, set the CAS policy permissions to enable the web part to run from /bin:&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;1. We are going to copy the existing wss_minimaltrust.config, rename it, then make our policy changes in that file. Go to c:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG, copy wss_minimaltrust.config and rename it wss_webpartbintrust.config.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. In our new CAS policy file, wss_webpartbintrust.config, we have three changes.&lt;br /&gt;&lt;br /&gt;a. First, add a reference to SharePointPermission in the &amp;lt;SecurityClasses&amp;gt; section. Add the following:&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt; &amp;lt;SecurityClass Name="SharePointPermission" Description="Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" /&amp;gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;b. Next, add a new custom permission set. Find the NamedPermissionSet that is named SPRestricted and copy and paste this below it:&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;   &amp;lt;PermissionSet class="NamedPermissionSet" version="1" Name="WebPartBinTrust"&amp;gt;&lt;br /&gt;      &amp;lt;IPermission class="AspNetHostingPermission" version="1" Level="Minimal" /&amp;gt;&lt;br /&gt;      &amp;lt;IPermission class="SecurityPermission" version="1" Flags="Execution" /&amp;gt;&lt;br /&gt;      &amp;lt;IPermission class="WebPartPermission" version="1" Connections="True" /&amp;gt;&lt;br /&gt;      &amp;lt;IPermission class="SharePointPermission" version="1" ObjectModel="True" /&amp;gt;&lt;br /&gt;   &amp;lt;/PermissionSet&amp;gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;c. Finally, add the code group. This will indicate when the permissions are to be set. It should be noted this can be done one of two ways, either using a strong name membership (assuming your assembly has been strongnamed) or a url membership, which would give you the option of having the permissions apply to all assemblies in the specified bin or the specific assembly itself.&lt;br /&gt;&lt;br /&gt;   Under &amp;lt;CodeGroup class="FirstMatchCodeGroup"...&amp;gt; you will see several &amp;lt;CodeGroup class="UnionCodeGroup"...&amp;gt; tags. Place the following code as the first one of these.&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;  &amp;lt;CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="WebPartBinTrust"&amp;gt;&lt;br /&gt;              &amp;lt;IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="00240000048000009400000006020000002400005253413100040000010001000DAF8ED8D945CD2ABB2EE7953A6039B791A725F11B4588AC6D70B3E0648F955E9ED4C3C43CB044B8B0E8A6FF4D4FFBE9E3B9297D45F688A7264534E12414E17539305207EC961DA94DF294E7722CCD9BDBFC95A896E996F57156705D281EC39280BD604E87724556AF5807D146963F19F5B43DB69E1F22695463153A553260D2" Name="ContactUsWebPart" /&amp;gt;&lt;br /&gt;            &amp;lt;/CodeGroup&amp;gt;&lt;/div&gt;(Note: The PublicKeyBlob must be the public key blob of your assembly. You may see the PublicKeyBlob in another tag referencing your assembly, in which case, just copy and paste it. Otherwise, you will need to run the command:  secutil -hex -s contactuswebpart.dll &gt; publickeyblob.txt, then copy and paste it from the output file publickeyblob.txt.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3. Almost there... Just two more changes, but this time in your site's web.config. Go to c:\inetpub\wwwroot\wss\VirtualDirectories\&amp;lt;your site&amp;gt;\web.config.&lt;br /&gt;&lt;br /&gt;a. In the SecurityPolicy tag, make sure there is an entry for a custom TrustLevel that points to the policy file we just edited:&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;&amp;lt;trustLevel name="WSS_Custom" policyFile="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\config\wss_webpartbintrust.config" /&amp;gt;&lt;/div&gt;&lt;br /&gt;b. Scroll down a bit and make sure the trust level is set like this: &lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;&amp;lt;trust level="WSS_Custom" originUrl="" /&amp;gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;That's it! Restart IIS and have fun!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Some good pages that helped me figure this all out:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.zimmergren.net/archive/2009/04/08/wspbuilder-walkthrough-of-the-visual-studio-add-in.aspx"&gt;WSPBuilder - Walkthrough of the Visual Studio Add-in&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.sharepointblogs.com/ssa/archive/2007/01/12/moss-2007-and-code-access-security.aspx"&gt;MOSS 2007 and Code Access Security&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://geekswithblogs.net/Gaurav/archive/2006/10/06/93353.aspx"&gt;Gaurav Taneja article on bin and GAC deployment&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://panvega.wordpress.com/2008/05/14/deploy-dlls-to-the-webapplications-bin-rather-than-gac/"&gt;Deploy dlls to the webApplications bin rather than GAC&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.combined-knowledge.com/Downloads/Code%20Access%20Security%20in%20SharePoint%202007%20for%20Administrators.pdf"&gt;Code Access Security in SharePoint 2007 for Administrators&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-1976245410104156626?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/1976245410104156626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/how-to-deploy-sharepoint-web-part-to.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1976245410104156626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1976245410104156626'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/how-to-deploy-sharepoint-web-part-to.html' title='How to deploy a SharePoint web part to bin'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8738915332067259184</id><published>2009-07-12T21:28:00.010-04:00</published><updated>2009-07-19T21:24:16.964-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='connected web parts'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='provider'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='consumer'/><title type='text'>How to create connected web parts: Part 2</title><content type='html'>Here in Part 2, you will find my tutorial on how to create Contact List/Email Contact connected web parts, along with entire code to be copy and pasted. I recommend following the tutorial, the at the end I will describe what's happening with respect to the transfer of a selected person from one web part to the other.&lt;br /&gt;&lt;br /&gt;At a high level:&lt;br /&gt;&lt;br /&gt;In the following project, we will create two web parts, a web part called Contact List that will display data from a custom list also called Contact List; and another web part called Email Contact, which will 1. save the message a user types into its form to a custom list called Sent Emails, then 2. email that message to the selected contact person. When a user clicks a contact in the Contact List web part, the person will then appear at the top of the Email Contact web part above the form the user can enter a message into. This transfer of a selected person from the Contact List web part to the Email Contact web part is what requires the web parts to be connected web parts. This connected web part setup also allows us the flexibility of displaying the Contact List by itself, or pairing it with other consumer web parts we develop down the road.&lt;br /&gt;&lt;br /&gt;In summary:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Contact List web part requires a custom list called Contact List. Contact List web part will be the provider web part, providing a person to a consumer web part, in this example it will be the Email Contact web part.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Email Contact web part requires a custom list called Sent Emails. Email Contact web part will be the consumer, consuming a person from a provider web part, in this example it will be the Contact List web part.&lt;/li&gt;&lt;li&gt;The two web parts will both use the same interface, we'll call it IContact.&lt;/li&gt;&lt;li&gt;Once both web parts are built and deployed, they can then be individually added to a page on your SharePoint site, then you will connect them by clicking either web part -&gt; Modify Shared Web Part -&gt; Connections&lt;/li&gt;&lt;/ol&gt;Ok, so that was the all the high-level stuff. Let's do this step by step from the beginning. All you have to do is click along and paste code.&lt;br /&gt;&lt;br /&gt;I. First, the easy part - we have to create the two custom lists Contact List&lt;br /&gt;and Sent Emails in your SharePoint site.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;In your SharePoint site, click Site Actions -&gt; Create -&gt; Custom List&lt;/li&gt;&lt;li&gt;for Name, type Contact List&lt;/li&gt;&lt;li&gt;click Create&lt;/li&gt;&lt;li&gt;click Contact List in the left menu&lt;br /&gt;&lt;/li&gt;&lt;li&gt;click Settings -&gt; List Settings&lt;/li&gt;&lt;li&gt;click Title column, rename to Contact Name, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Job Title, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Email Address, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Phone, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Fax, click OK&lt;/li&gt;&lt;li&gt;Now that you have created the contact list, enter in 2 or 3 contacts by clicking Contact List, then New -&gt; New Item, then fill out ALL the fields (including the phone and fax) and click OK (I still need to add error handling on the web part if a value is empty)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In your SharePoint site, click Site Actions -&gt; Create -&gt; Custom List&lt;/li&gt;&lt;li&gt;for Name, type Sent Emails&lt;br /&gt;&lt;/li&gt;&lt;li&gt;click Create&lt;/li&gt;&lt;li&gt;click Sent Emails in the left menu&lt;br /&gt;&lt;/li&gt;&lt;li&gt;click Settings -&gt; List Settings&lt;/li&gt;&lt;li&gt;click Title column,  and rename to Message Subject, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Submitter Name, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Submitter Email, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Message, click OK&lt;/li&gt;&lt;li&gt;click Create Column, for Column name type Contact Email, click OK&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;II. Now, we create the interface and web parts in Visual Studio 2008.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;load Visual Studio 2008&lt;/li&gt;&lt;li&gt;File -&gt; New -&gt; Project -&gt; Visual Basic -&gt; SharePoint -&gt; Web Part&lt;/li&gt;&lt;li&gt;for Name and Solution Name, type: ContactUs&lt;/li&gt;&lt;li&gt;Click OK&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click the project name ContactUs, then click Properties&lt;/li&gt;&lt;li&gt;Click the Debug tab and in "Start browser with URL:", type the SharePoint site you want these web parts to be deployed to&lt;/li&gt;&lt;li&gt;Click X to close out of Properties&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click WebPart1 and click Delete, then OK&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click project ContactUs, then click Add -&gt; New Item -&gt; SharePoint -&gt; Web Part and for the name type ContactList, then click Add&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click project ContactUs, then click Add -&gt; New Item -&gt; SharePoint -&gt; Web Part and for the name type ContactList, then click Add&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click project ContactUs, then click Add -&gt; New Item -&gt; Code -&gt; Interface and for the name type IContact.vb, then click Add&lt;/li&gt;&lt;li&gt;In IContact.vb, copy and paste the following code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;Property ContactName() As String&lt;br /&gt;Property JobTitle() As String&lt;br /&gt;Property EmailAddress() As String&lt;br /&gt;Property Phone() As String&lt;br /&gt;Property Fax() As String&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In Solution Explorer, right-click References, then click Add Reference; in the .NET tab, select System.Drawing and click OK&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In ContactList.vb, comment out the first two lines, Option Explicit On and Option Strict On&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In ContactList.vb, add the following code directly below Public Class ContactList (delete the other pre-generated code within the class):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;        Inherits System.Web.UI.WebControls.WebParts.WebPart&lt;br /&gt;Implements IContact&lt;br /&gt;&lt;br /&gt;WithEvents oContactNameLink As LinkButton&lt;br /&gt;Private ContactNameToEmail As String = String.Empty&lt;br /&gt;Private JobTitleToEmail As String = String.Empty&lt;br /&gt;Private EmailAddressToEmail As String = String.Empty&lt;br /&gt;Private PhoneToEmail As String = String.Empty&lt;br /&gt;Private FaxToEmail As String = String.Empty&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dim strContactListDataSource As String = ""&lt;br /&gt;&lt;br /&gt;&amp;lt;WebBrowsable(True), Personalizable(True), FriendlyName("Contact List Data Source"), SPWebCategoryName("Data")&amp;gt; _&lt;br /&gt;Property ContactListDataSource() As String&lt;br /&gt;Get&lt;br /&gt;    Return strContactListDataSource&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    strContactListDataSource = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Public Sub New()&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Personalizable()&amp;gt; _&lt;br /&gt;Public Property ContactName() As String _&lt;br /&gt;Implements IContact.ContactName&lt;br /&gt;&lt;br /&gt;Get&lt;br /&gt;    Return ContactNameToEmail&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    ContactNameToEmail = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Personalizable()&amp;gt; _&lt;br /&gt;Public Property JobTitle() As String _&lt;br /&gt;Implements IContact.JobTitle&lt;br /&gt;&lt;br /&gt;Get&lt;br /&gt;    Return JobTitleToEmail&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    JobTitleToEmail = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Personalizable()&amp;gt; _&lt;br /&gt;Public Property EmailAddress() As String _&lt;br /&gt;Implements IContact.EmailAddress&lt;br /&gt;&lt;br /&gt;Get&lt;br /&gt;    Return EmailAddressToEmail&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    EmailAddressToEmail = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Personalizable()&amp;gt; _&lt;br /&gt;Public Property Phone() As String _&lt;br /&gt;Implements IContact.Phone&lt;br /&gt;&lt;br /&gt;Get&lt;br /&gt;    Return PhoneToEmail&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    PhoneToEmail = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Personalizable()&amp;gt; _&lt;br /&gt;Public Property Fax() As String _&lt;br /&gt;Implements IContact.Fax&lt;br /&gt;&lt;br /&gt;Get&lt;br /&gt;    Return FaxToEmail&lt;br /&gt;End Get&lt;br /&gt;Set(ByVal value As String)&lt;br /&gt;    FaxToEmail = value&lt;br /&gt;End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' This is the callback method that returns the provider.&lt;br /&gt;&amp;lt;ConnectionProvider("Contact Provider", "ContactProvider")&amp;gt; _&lt;br /&gt;Public Function ProvideIContact() As IContact&lt;br /&gt;Return Me&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Protected Overrides Sub CreateChildControls()&lt;br /&gt;MyBase.CreateChildControls()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;'if ContactListSource is a valid list, display controls&lt;br /&gt;If ContactListIsValid() Then&lt;br /&gt;&lt;br /&gt;    Try&lt;br /&gt;        'get the site the web part is running on&lt;br /&gt;        Dim oWeb As SPWeb&lt;br /&gt;        oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;        'get the list we are going to work with&lt;br /&gt;        Dim oList As SPList&lt;br /&gt;        oList = oWeb.Lists(ContactListDataSource)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        'create table object&lt;br /&gt;        Dim oTable As New Table&lt;br /&gt;        'oTable.Width = 100&lt;br /&gt;        oTable.CellPadding = 0&lt;br /&gt;        oTable.CellSpacing = 0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        'add the table row&lt;br /&gt;        Dim oRow As New TableRow&lt;br /&gt;        oTable.Rows.Add(oRow)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        'add the cells&lt;br /&gt;        Dim oCellHeaderName As New TableCell&lt;br /&gt;        oCellHeaderName.Width = 180&lt;br /&gt;        oCellHeaderName.Text = "Name"&lt;br /&gt;        oRow.Cells.Add(oCellHeaderName)&lt;br /&gt;&lt;br /&gt;        Dim oCellHeaderJobTitle As New TableCell&lt;br /&gt;        oCellHeaderJobTitle.Width = 180&lt;br /&gt;        oCellHeaderJobTitle.Text = "Job Title"&lt;br /&gt;        oRow.Cells.Add(oCellHeaderJobTitle)&lt;br /&gt;&lt;br /&gt;        Dim oCellHeaderEmail As New TableCell&lt;br /&gt;        oCellHeaderEmail.Width = 180&lt;br /&gt;        oCellHeaderEmail.Text = "Email Address"&lt;br /&gt;        oRow.Cells.Add(oCellHeaderEmail)&lt;br /&gt;&lt;br /&gt;        Dim oCellHeaderPhone As New TableCell&lt;br /&gt;        oCellHeaderPhone.Width = 100&lt;br /&gt;        oCellHeaderPhone.Text = "Phone"&lt;br /&gt;        oRow.Cells.Add(oCellHeaderPhone)&lt;br /&gt;&lt;br /&gt;        Dim oCellHeaderFax As New TableCell&lt;br /&gt;        oCellHeaderFax.Width = 100&lt;br /&gt;        oCellHeaderFax.Text = "Fax"&lt;br /&gt;        oRow.Cells.Add(oCellHeaderFax)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        'for all rows in list, display them&lt;br /&gt;        For Each oContactListItem As SPListItem In oList.Items&lt;br /&gt;            'add the table row for the new list item&lt;br /&gt;            Dim oContactRow As New TableRow&lt;br /&gt;            oTable.Rows.Add(oContactRow)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            Dim strPhone As String = ""&lt;br /&gt;            If Not oContactListItem.Item("Phone") Is Nothing Then&lt;br /&gt;                strPhone = oContactListItem.Item("Phone").ToString&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            Dim strFax As String = ""&lt;br /&gt;            If Not oContactListItem.Item("Phone") Is Nothing Then&lt;br /&gt;                strFax = oContactListItem.Item("Fax").ToString&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            'add the cells&lt;br /&gt;            Dim oCellName As New TableCell&lt;br /&gt;            oContactNameLink = New LinkButton&lt;br /&gt;            oContactNameLink.Text = oContactListItem.Item("Contact Name").ToString&lt;br /&gt;            oContactNameLink.CommandArgument = oContactListItem.Item("Job Title").ToString &amp;amp; "|" &amp;amp; _&lt;br /&gt;                                         oContactListItem.Item("Email Address").ToString &amp;amp; "|" &amp;amp; _&lt;br /&gt;                                         strPhone &amp;amp; "|" &amp;amp; _&lt;br /&gt;                                         strFax &amp;amp; "|"&lt;br /&gt;            AddHandler oContactNameLink.Click, AddressOf ContactSelected&lt;br /&gt;            oCellName.Controls.Add(oContactNameLink)&lt;br /&gt;            oContactRow.Cells.Add(oCellName)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            Dim oCellJobTitle As New TableCell&lt;br /&gt;            oCellJobTitle.Text = oContactListItem.Item("Job Title").ToString&lt;br /&gt;            oContactRow.Cells.Add(oCellJobTitle)&lt;br /&gt;&lt;br /&gt;            Dim oCellEmailAddress As New TableCell&lt;br /&gt;            oCellEmailAddress.Text = oContactListItem.Item("Email Address").ToString&lt;br /&gt;            oContactRow.Cells.Add(oCellEmailAddress)&lt;br /&gt;&lt;br /&gt;            Dim oCellPhone As New TableCell&lt;br /&gt;            If Not oContactListItem.Item("Phone") Is Nothing Then&lt;br /&gt;                oCellPhone.Text = oContactListItem.Item("Phone").ToString&lt;br /&gt;            Else&lt;br /&gt;                oCellPhone.Text = ""&lt;br /&gt;            End If&lt;br /&gt;            oContactRow.Cells.Add(oCellPhone)&lt;br /&gt;&lt;br /&gt;            Dim oCellFax As New TableCell&lt;br /&gt;            If Not oContactListItem.Item("Fax") Is Nothing Then&lt;br /&gt;                oCellFax.Text = oContactListItem.Item("Fax").ToString&lt;br /&gt;            Else&lt;br /&gt;                oCellFax.Text = ""&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            oContactRow.Cells.Add(oCellFax)&lt;br /&gt;&lt;br /&gt;        Next&lt;br /&gt;&lt;br /&gt;        'add the table to the web part controls collection&lt;br /&gt;        Controls.Add(oTable)&lt;br /&gt;&lt;br /&gt;    Catch ex As Exception&lt;br /&gt;        Dim lblErrorMessage As New Label&lt;br /&gt;        lblErrorMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;        lblErrorMessage.Text = "Error: " &amp;amp; ex.Message &amp;amp; _&lt;br /&gt;                  "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Details: " &amp;amp; ex.ToString&lt;br /&gt;        Controls.Add(lblErrorMessage)&lt;br /&gt;&lt;br /&gt;    End Try&lt;br /&gt;&lt;br /&gt;Else&lt;br /&gt;    'else display message telling user they must configure the ContactListDataSource property&lt;br /&gt;    Dim lblConfigureMessage As New Label&lt;br /&gt;    lblConfigureMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;    lblConfigureMessage.Text = "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "You must configure the Contact List Data Source property. " &amp;amp; _&lt;br /&gt;                               "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Please click Modify Shared Web Part -&gt; Data -&gt; Contact List Data Source " &amp;amp; _&lt;br /&gt;                                            "and set it to a list based on the ContactListTemplate list template."&lt;br /&gt;    Controls.Add(lblConfigureMessage)&lt;br /&gt;End If&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Sub ContactSelected(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles oContactNameLink.Click&lt;br /&gt;   'when a contact is selected, assign their info to local variables&lt;br /&gt;&lt;br /&gt;   Dim ContactFields As String&lt;br /&gt;   Dim ContactFieldsArray(4) As String&lt;br /&gt;&lt;br /&gt;   'assign person's name to local variable ContactNameToEmail&lt;br /&gt;   ContactNameToEmail = CType(sender, LinkButton).Text&lt;br /&gt;&lt;br /&gt;   'pull contact fields from button's CommandArgument property&lt;br /&gt;   ContactFields = CType(sender, LinkButton).CommandArgument&lt;br /&gt;&lt;br /&gt;   'split contact fields into an array&lt;br /&gt;   ContactFieldsArray = ContactFields.Split("|")&lt;br /&gt;&lt;br /&gt;   'assign contact fields to local variables&lt;br /&gt;   JobTitleToEmail = ContactFieldsArray(0)&lt;br /&gt;   EmailAddressToEmail = ContactFieldsArray(1)&lt;br /&gt;   PhoneToEmail = ContactFieldsArray(2)&lt;br /&gt;   FaxToEmail = ContactFieldsArray(3)&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Function ContactListIsValid() As Boolean&lt;br /&gt;'PURPOSE: returns true if specified contact list is valid for the web part&lt;br /&gt;&lt;br /&gt;Try&lt;br /&gt;    'get the site the web part is running on&lt;br /&gt;    Dim oWeb As SPWeb&lt;br /&gt;    oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;    'get the list specified by user in property, if list is not found in site, flow will hit Catch&lt;br /&gt;    Dim oList As SPList&lt;br /&gt;    oList = oWeb.Lists(ContactListDataSource)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    'check if fields exist in list&lt;br /&gt;&lt;br /&gt;    Dim blnContactNameFound As Boolean = False&lt;br /&gt;    Dim blnJobTitleFound As Boolean = False&lt;br /&gt;    Dim blnEmailAddressFound As Boolean = False&lt;br /&gt;    Dim blnPhoneFound As Boolean = False&lt;br /&gt;    Dim blnFaxFound As Boolean = False&lt;br /&gt;&lt;br /&gt;    'loop through the fields in the list and check to see if all the required fields are in the list&lt;br /&gt;    For Each oField As SPField In oList.Fields&lt;br /&gt;        If Not oField.Hidden And Not oField.ReadOnlyField And oField.Type &lt;&gt; SPFieldType.Attachments Then&lt;br /&gt;&lt;br /&gt;            If oField.Title = "Contact Name" Then&lt;br /&gt;                blnContactNameFound = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            If oField.Title = "Job Title" Then&lt;br /&gt;                blnJobTitleFound = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            If oField.Title = "Email Address" Then&lt;br /&gt;                blnEmailAddressFound = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            If oField.Title = "Phone" Then&lt;br /&gt;                blnPhoneFound = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;            If oField.Title = "Fax" Then&lt;br /&gt;                blnFaxFound = True&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        End If&lt;br /&gt;    Next&lt;br /&gt;&lt;br /&gt;    'if all fields were found, return true&lt;br /&gt;    If blnContactNameFound And blnJobTitleFound And blnEmailAddressFound And blnPhoneFound And blnFaxFound Then&lt;br /&gt;        Return True&lt;br /&gt;    Else&lt;br /&gt;        Return False&lt;br /&gt;    End If&lt;br /&gt;&lt;br /&gt;Catch ex As Exception&lt;br /&gt;    Return False&lt;br /&gt;&lt;br /&gt;End Try&lt;br /&gt;&lt;br /&gt;End Function&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In EmailContact.vb, add the following code below the Imports statements at the top of the file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;Imports System.Net.Mail&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In EmailContact.vb, add the following code below Public Class EmailContact (delete the other pre-generated code within the class):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div face="courier new" style="background-color: rgb(255, 213, 149);"&gt;        Inherits System.Web.UI.WebControls.WebParts.WebPart&lt;br /&gt;&lt;br /&gt;Private _provider As IContact&lt;br /&gt;Private _contactName As String&lt;br /&gt;Private _jobTitle As String&lt;br /&gt;Private _emailAddress As String&lt;br /&gt;Private _phone As String&lt;br /&gt;Private _fax As String&lt;br /&gt;&lt;br /&gt;Private lblContactNameValue As Label&lt;br /&gt;Private lblJobTitleValue As Label&lt;br /&gt;Private lblEmailAddressValue As Label&lt;br /&gt;Private lblPhoneValue As Label&lt;br /&gt;Private lblFaxValue As Label&lt;br /&gt;&lt;br /&gt;Dim oMessageSubject As TextBox&lt;br /&gt;Dim oSubmitterName As TextBox&lt;br /&gt;Dim oSubmitterEmail As TextBox&lt;br /&gt;Dim oMessage As TextBox&lt;br /&gt;&lt;br /&gt;Dim lblStatusMessage As New Label&lt;br /&gt;&lt;br /&gt;WithEvents btnSave As New Button&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dim strEmailContactDestination As String = ""&lt;br /&gt;&lt;br /&gt;&amp;lt;WebBrowsable(True), Personalizable(True), FriendlyName("Email Contact Destination"), SPWebCategoryName("Data")&amp;gt; _&lt;br /&gt;Property EmailContactDestination() As String&lt;br /&gt;   Get&lt;br /&gt;       Return strEmailContactDestination&lt;br /&gt;   End Get&lt;br /&gt;   Set(ByVal value As String)&lt;br /&gt;       strEmailContactDestination = value&lt;br /&gt;   End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dim strSMTPMailHost As String = ""&lt;br /&gt;&lt;br /&gt;&amp;lt;WebBrowsable(True), Personalizable(True), FriendlyName("SMTP Mail Host"), SPWebCategoryName("Data")&amp;gt; _&lt;br /&gt;Property SMTPMailHost() As String&lt;br /&gt;   Get&lt;br /&gt;       Return strSMTPMailHost&lt;br /&gt;   End Get&lt;br /&gt;   Set(ByVal value As String)&lt;br /&gt;       strSMTPMailHost = value&lt;br /&gt;   End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;'Public Sub New()&lt;br /&gt;'End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' This method is identified by the ConnectionConsumer&lt;br /&gt;' attribute, and is the mechanism for connecting with&lt;br /&gt;' the provider.&lt;br /&gt;&amp;lt;ConnectionConsumer("Contact Consumer", "ContactConsumer")&amp;gt; _&lt;br /&gt;Public Sub GetIContact(ByVal Provider As IContact)&lt;br /&gt;   _provider = Provider&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Protected Overrides Sub CreateChildControls()&lt;br /&gt;   MyBase.CreateChildControls()&lt;br /&gt;&lt;br /&gt;   'if EmailContactDestination is a valid list, display controls&lt;br /&gt;   If EmailContactDestinationIsValid() Then&lt;br /&gt;       If SMTPMailHostIsValid() Then&lt;br /&gt;           'create table object&lt;br /&gt;           Dim oTable As New Table&lt;br /&gt;           oTable.CellPadding = 0&lt;br /&gt;           oTable.CellSpacing = 0&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oContactNameRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oContactNameRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oContactNameLabelCell As New TableCell&lt;br /&gt;           oContactNameRow.Cells.Add(oContactNameLabelCell)&lt;br /&gt;           Dim oContactNameValueCell As New TableCell&lt;br /&gt;           oContactNameRow.Cells.Add(oContactNameValueCell)&lt;br /&gt;&lt;br /&gt;           oContactNameLabelCell.Width = 125&lt;br /&gt;           oContactNameValueCell.Width = 350&lt;br /&gt;&lt;br /&gt;           'create label objects for field name and field value&lt;br /&gt;           Dim lblContactNameHeader As New Label&lt;br /&gt;           lblContactNameHeader.Text = "Contact Name"&lt;br /&gt;           lblContactNameValue = New Label&lt;br /&gt;           lblContactNameValue.Text = ""&lt;br /&gt;&lt;br /&gt;           'add the controls to the cells&lt;br /&gt;           oContactNameLabelCell.Controls.Add(lblContactNameHeader)&lt;br /&gt;           oContactNameValueCell.Controls.Add(lblContactNameValue)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oJobTitleRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oJobTitleRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oJobTitleLabelCell As New TableCell&lt;br /&gt;           oJobTitleRow.Cells.Add(oJobTitleLabelCell)&lt;br /&gt;           Dim oJobTitleValueCell As New TableCell&lt;br /&gt;           oJobTitleRow.Cells.Add(oJobTitleValueCell)&lt;br /&gt;&lt;br /&gt;           'create label objects for field name and field value&lt;br /&gt;           Dim lblJobTitleHeader As New Label&lt;br /&gt;           lblJobTitleHeader.Text = "Job Title"&lt;br /&gt;           lblJobTitleValue = New Label&lt;br /&gt;           lblJobTitleValue.Text = ""&lt;br /&gt;&lt;br /&gt;           'add the controls to the cells&lt;br /&gt;           oJobTitleLabelCell.Controls.Add(lblJobTitleHeader)&lt;br /&gt;           oJobTitleValueCell.Controls.Add(lblJobTitleValue)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oEmailAddressRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oEmailAddressRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oEmailAddressLabelCell As New TableCell&lt;br /&gt;           oEmailAddressRow.Cells.Add(oEmailAddressLabelCell)&lt;br /&gt;           Dim oEmailAddressValueCell As New TableCell&lt;br /&gt;           oEmailAddressRow.Cells.Add(oEmailAddressValueCell)&lt;br /&gt;&lt;br /&gt;           'create label objects for field name and field value&lt;br /&gt;           Dim lblEmailAddressHeader As New Label&lt;br /&gt;           lblEmailAddressHeader.Text = "Email Address"&lt;br /&gt;           lblEmailAddressValue = New Label&lt;br /&gt;           lblEmailAddressValue.Text = ""&lt;br /&gt;&lt;br /&gt;           'add the controls to the cells&lt;br /&gt;           oEmailAddressLabelCell.Controls.Add(lblEmailAddressHeader)&lt;br /&gt;           oEmailAddressValueCell.Controls.Add(lblEmailAddressValue)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oPhoneRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oPhoneRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oPhoneLabelCell As New TableCell&lt;br /&gt;           oPhoneRow.Cells.Add(oPhoneLabelCell)&lt;br /&gt;           Dim oPhoneValueCell As New TableCell&lt;br /&gt;           oPhoneRow.Cells.Add(oPhoneValueCell)&lt;br /&gt;&lt;br /&gt;           'create label objects for field name and field value&lt;br /&gt;           Dim lblPhoneHeader As New Label&lt;br /&gt;           lblPhoneHeader.Text = "Phone"&lt;br /&gt;           lblPhoneValue = New Label&lt;br /&gt;           lblPhoneValue.Text = ""&lt;br /&gt;&lt;br /&gt;           'add the controls to the cells&lt;br /&gt;           oPhoneLabelCell.Controls.Add(lblPhoneHeader)&lt;br /&gt;           oPhoneValueCell.Controls.Add(lblPhoneValue)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oFaxRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oFaxRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oFaxLabelCell As New TableCell&lt;br /&gt;           oFaxRow.Cells.Add(oFaxLabelCell)&lt;br /&gt;           Dim oFaxValueCell As New TableCell&lt;br /&gt;           oFaxRow.Cells.Add(oFaxValueCell)&lt;br /&gt;&lt;br /&gt;           'create label objects for field name and field value&lt;br /&gt;           Dim lblFaxHeader As New Label&lt;br /&gt;           lblFaxHeader.Text = "Fax"&lt;br /&gt;           lblFaxValue = New Label&lt;br /&gt;           lblFaxValue.Text = ""&lt;br /&gt;&lt;br /&gt;           'add the controls to the cells&lt;br /&gt;           oFaxLabelCell.Controls.Add(lblFaxHeader)&lt;br /&gt;           oFaxValueCell.Controls.Add(lblFaxValue)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'build form to allow user to submit comments and have them emailed to selected contact&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oMessageSubjectRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oMessageSubjectRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oMessageSubjectLabelCell As New TableCell&lt;br /&gt;           oMessageSubjectRow.Cells.Add(oMessageSubjectLabelCell)&lt;br /&gt;           Dim oMessageSubjectValueCell As New TableCell&lt;br /&gt;           oMessageSubjectRow.Cells.Add(oMessageSubjectValueCell)&lt;br /&gt;&lt;br /&gt;           'create the field label control&lt;br /&gt;           Dim lblMessageSubject As New Label&lt;br /&gt;           lblMessageSubject.Text = "Message Subject"&lt;br /&gt;&lt;br /&gt;           'create the form field control&lt;br /&gt;           oMessageSubject = New TextBox&lt;br /&gt;           oMessageSubject.Width = 300&lt;br /&gt;&lt;br /&gt;           'create validator&lt;br /&gt;           Dim oMessageSubjectRequired As New RequiredFieldValidator&lt;br /&gt;&lt;br /&gt;           'add controls to the cells&lt;br /&gt;           oMessageSubjectLabelCell.Controls.Add(lblMessageSubject)&lt;br /&gt;           oMessageSubjectValueCell.Controls.Add(oMessageSubject)&lt;br /&gt;           oMessageSubjectValueCell.Controls.Add(oMessageSubjectRequired)&lt;br /&gt;&lt;br /&gt;           'configure validator&lt;br /&gt;           oMessageSubjectRequired.ErrorMessage = " * required"&lt;br /&gt;           oMessageSubject.ID = "oMessageSubject"&lt;br /&gt;           oMessageSubjectRequired.Display = ValidatorDisplay.Dynamic&lt;br /&gt;           oMessageSubjectRequired.ControlToValidate = oMessageSubject.ID&lt;br /&gt;           oMessageSubjectRequired.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oSubmitterNameRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oSubmitterNameRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oSubmitterNameLabelCell As New TableCell&lt;br /&gt;           oSubmitterNameRow.Cells.Add(oSubmitterNameLabelCell)&lt;br /&gt;           Dim oSubmitterNameValueCell As New TableCell&lt;br /&gt;           oSubmitterNameRow.Cells.Add(oSubmitterNameValueCell)&lt;br /&gt;&lt;br /&gt;           'create the field label control&lt;br /&gt;           Dim lblSubmitterName As New Label&lt;br /&gt;           lblSubmitterName.Text = "Submitter Name"&lt;br /&gt;&lt;br /&gt;           'create the form field control&lt;br /&gt;           oSubmitterName = New TextBox&lt;br /&gt;           oSubmitterName.Width = 300&lt;br /&gt;&lt;br /&gt;           'create validator&lt;br /&gt;           Dim oSubmitterNameRequired As New RequiredFieldValidator&lt;br /&gt;&lt;br /&gt;           'add controls to the cells&lt;br /&gt;           oSubmitterNameLabelCell.Controls.Add(lblSubmitterName)&lt;br /&gt;           oSubmitterNameValueCell.Controls.Add(oSubmitterName)&lt;br /&gt;           oSubmitterNameValueCell.Controls.Add(oSubmitterNameRequired)&lt;br /&gt;&lt;br /&gt;           'configure validator&lt;br /&gt;           oSubmitterNameRequired.ErrorMessage = " * required"&lt;br /&gt;           oSubmitterName.ID = "oSubmitterName"&lt;br /&gt;           oSubmitterNameRequired.Display = ValidatorDisplay.Dynamic&lt;br /&gt;           oSubmitterNameRequired.ControlToValidate = oSubmitterName.ID&lt;br /&gt;           oSubmitterNameRequired.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oSubmitterEmailRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oSubmitterEmailRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oSubmitterEmailLabelCell As New TableCell&lt;br /&gt;           oSubmitterEmailRow.Cells.Add(oSubmitterEmailLabelCell)&lt;br /&gt;           Dim oSubmitterEmailValueCell As New TableCell&lt;br /&gt;           oSubmitterEmailRow.Cells.Add(oSubmitterEmailValueCell)&lt;br /&gt;&lt;br /&gt;           'create the field label control&lt;br /&gt;           Dim lblSubmitterEmail As New Label&lt;br /&gt;           lblSubmitterEmail.Text = "Submitter Email"&lt;br /&gt;&lt;br /&gt;           'create the form field control&lt;br /&gt;           oSubmitterEmail = New TextBox&lt;br /&gt;           oSubmitterEmail.Width = 300&lt;br /&gt;&lt;br /&gt;           'create validator&lt;br /&gt;           Dim oSubmitterEmailRequired As New RequiredFieldValidator&lt;br /&gt;           Dim oSubmitterEmailValid As New RegularExpressionValidator&lt;br /&gt;&lt;br /&gt;           'add controls to the cells&lt;br /&gt;           oSubmitterEmailLabelCell.Controls.Add(lblSubmitterEmail)&lt;br /&gt;           oSubmitterEmailValueCell.Controls.Add(oSubmitterEmail)&lt;br /&gt;           oSubmitterEmailValueCell.Controls.Add(oSubmitterEmailRequired)&lt;br /&gt;           oSubmitterEmailValueCell.Controls.Add(oSubmitterEmailValid)&lt;br /&gt;&lt;br /&gt;           'configure validator&lt;br /&gt;           oSubmitterEmailRequired.ErrorMessage = " * required"&lt;br /&gt;           oSubmitterEmail.ID = "oSubmitterEmail"&lt;br /&gt;           oSubmitterEmailRequired.Display = ValidatorDisplay.Dynamic&lt;br /&gt;           oSubmitterEmailRequired.ControlToValidate = oSubmitterEmail.ID&lt;br /&gt;           oSubmitterEmailRequired.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;           'configure validator&lt;br /&gt;           oSubmitterEmailValid.ErrorMessage = " * Not a valid email address"&lt;br /&gt;           oSubmitterEmailValid.ControlToValidate = oSubmitterEmail.ID&lt;br /&gt;           oSubmitterEmailValid.Display = ValidatorDisplay.Dynamic&lt;br /&gt;           oSubmitterEmailValid.ValidationExpression = "\w+\w*\@\w+\w+\w*\.(com|edu|org|gov|net|corp)"&lt;br /&gt;           oSubmitterEmailValid.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create a row&lt;br /&gt;           Dim oMessageRow As New TableRow&lt;br /&gt;           oTable.Rows.Add(oMessageRow)&lt;br /&gt;&lt;br /&gt;           'create the cells and add them to the row&lt;br /&gt;           Dim oMessageLabelCell As New TableCell&lt;br /&gt;           oMessageRow.Cells.Add(oMessageLabelCell)&lt;br /&gt;           Dim oMessageValueCell As New TableCell&lt;br /&gt;           oMessageRow.Cells.Add(oMessageValueCell)&lt;br /&gt;&lt;br /&gt;           'create the field label control&lt;br /&gt;           Dim lblMessage As New Label&lt;br /&gt;           lblMessage.Text = "Message"&lt;br /&gt;&lt;br /&gt;           'create the form field control&lt;br /&gt;           oMessage = New TextBox&lt;br /&gt;           oMessage.Width = 300&lt;br /&gt;           oMessage.TextMode = TextBoxMode.MultiLine&lt;br /&gt;           oMessage.Rows = 4&lt;br /&gt;&lt;br /&gt;           'create validator&lt;br /&gt;           Dim oMessageRequired As New RequiredFieldValidator&lt;br /&gt;&lt;br /&gt;           'add controls to the cells&lt;br /&gt;           oMessageLabelCell.Controls.Add(lblMessage)&lt;br /&gt;           oMessageValueCell.Controls.Add(oMessage)&lt;br /&gt;           oMessageValueCell.Controls.Add(oMessageRequired)&lt;br /&gt;&lt;br /&gt;           'configure validator&lt;br /&gt;           oMessageRequired.ErrorMessage = " * required"&lt;br /&gt;           oMessage.ID = "oMessage"&lt;br /&gt;           oMessageRequired.Display = ValidatorDisplay.Dynamic&lt;br /&gt;           oMessageRequired.ControlToValidate = oMessage.ID&lt;br /&gt;           oMessageRequired.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'set the text for the save button&lt;br /&gt;           btnSave.Text = "Send"&lt;br /&gt;           btnSave.Enabled = False&lt;br /&gt;           btnSave.ValidationGroup = "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;           'create the row for the save button&lt;br /&gt;           Dim oRowButton As New TableRow&lt;br /&gt;           oTable.Rows.Add(oRowButton)&lt;br /&gt;&lt;br /&gt;           'create the cell for the save button&lt;br /&gt;           Dim oCellButton As New TableCell&lt;br /&gt;           oCellButton.ColumnSpan = 2&lt;br /&gt;           oCellButton.HorizontalAlign = Web.UI.WebControls.HorizontalAlign.Right&lt;br /&gt;           oRowButton.Cells.Add(oCellButton)&lt;br /&gt;&lt;br /&gt;           'add save button to cell&lt;br /&gt;           oCellButton.Controls.Add(btnSave)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'create the row for the status message&lt;br /&gt;           Dim oRowStatus As New TableRow&lt;br /&gt;           oTable.Rows.Add(oRowStatus)&lt;br /&gt;&lt;br /&gt;           'create the cell for the status message&lt;br /&gt;           Dim oCellStatus As New TableCell&lt;br /&gt;           oCellStatus.ColumnSpan = 2&lt;br /&gt;           oCellStatus.HorizontalAlign = Web.UI.WebControls.HorizontalAlign.Center&lt;br /&gt;           oRowStatus.Cells.Add(oCellStatus)&lt;br /&gt;&lt;br /&gt;           'add status message to cell&lt;br /&gt;           lblStatusMessage.ForeColor = Drawing.Color.Green&lt;br /&gt;           oCellStatus.Controls.Add(lblStatusMessage)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;           'add the table to the web part controls collection&lt;br /&gt;           Controls.Add(oTable)&lt;br /&gt;       Else&lt;br /&gt;           'else display message telling user they must configure the SMTPMailHost property&lt;br /&gt;           Dim lblConfigureMessage As New Label&lt;br /&gt;           lblConfigureMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;           lblConfigureMessage.Text = "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "You must configure the SMTP Mail Host property. " &amp;amp; _&lt;br /&gt;                                      "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Please click Modify Shared Web Part -&gt; Data -&gt; SMTP Mail Host " &amp;amp; _&lt;br /&gt;                                                   "and set it to a valid SMTP mail host."&lt;br /&gt;           Controls.Add(lblConfigureMessage)&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;   Else&lt;br /&gt;       'else display message telling user they must configure the EmailContactDestination property&lt;br /&gt;       Dim lblConfigureMessage As New Label&lt;br /&gt;       lblConfigureMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;       lblConfigureMessage.Text = "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "You must configure the Email Contact Destination property. " &amp;amp; _&lt;br /&gt;                                  "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Please click Modify Shared Web Part -&gt; Data -&gt; Email Contact Destination " &amp;amp; _&lt;br /&gt;                                               "and set it to a list based on the SentEmailsTemplate list template."&lt;br /&gt;       Controls.Add(lblConfigureMessage)&lt;br /&gt;   End If&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Protected Overrides Sub OnPreRender(ByVal e As EventArgs)&lt;br /&gt;   EnsureChildControls()&lt;br /&gt;&lt;br /&gt;   If Not (Me._provider Is Nothing) Then&lt;br /&gt;       'assign contact values to local variables&lt;br /&gt;       _contactName = _provider.ContactName.Trim()&lt;br /&gt;       _jobTitle = _provider.JobTitle.Trim()&lt;br /&gt;       _emailAddress = _provider.EmailAddress.Trim()&lt;br /&gt;       _phone = _provider.Phone.Trim()&lt;br /&gt;       _fax = _provider.Fax.Trim()&lt;br /&gt;&lt;br /&gt;       'assign contact values to labels on form&lt;br /&gt;       If _contactName &lt;&gt; "" Then&lt;br /&gt;           lblContactNameValue.Text = _contactName&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If _jobTitle &lt;&gt; "" Then&lt;br /&gt;           lblJobTitleValue.Text = _jobTitle&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If _emailAddress &lt;&gt; "" Then&lt;br /&gt;           lblEmailAddressValue.Text = _emailAddress&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If _phone &lt;&gt; "" Then&lt;br /&gt;           lblPhoneValue.Text = _phone&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If _fax &lt;&gt; "" Then&lt;br /&gt;           lblFaxValue.Text = _fax&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;       'enable save button if there is a value passed in otherwise disable button&lt;br /&gt;       If _contactName = "" Then&lt;br /&gt;           btnSave.Enabled = False&lt;br /&gt;&lt;br /&gt;           'clear form&lt;br /&gt;           ClearForm()&lt;br /&gt;       Else&lt;br /&gt;           btnSave.Enabled = True&lt;br /&gt;&lt;br /&gt;           'clear status message&lt;br /&gt;           lblStatusMessage.Text = ""&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;   End If&lt;br /&gt;&lt;br /&gt;End Sub 'OnPreRender&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sub SaveAndEmail() Handles btnSave.Click&lt;br /&gt;   'save message to the specified list library&lt;br /&gt;   SaveMessage()&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Sub SaveMessage()&lt;br /&gt;   'PURPOSE: save values from form into list library ContactUs&lt;br /&gt;&lt;br /&gt;   Try&lt;br /&gt;       'get the site the web part is running on&lt;br /&gt;       Dim oWeb As SPWeb&lt;br /&gt;       oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;       'get the list where we want to save the form values to&lt;br /&gt;       Dim oList As SPList&lt;br /&gt;       oList = oWeb.Lists(EmailContactDestination)&lt;br /&gt;&lt;br /&gt;       'create a new list item&lt;br /&gt;       Dim oNewItem As SPListItem&lt;br /&gt;       oNewItem = oList.Items.Add()&lt;br /&gt;&lt;br /&gt;       'populate the new list item with values from the form&lt;br /&gt;       oNewItem.Item("Message Subject") = oMessageSubject.Text&lt;br /&gt;       oNewItem.Item("Submitter Name") = oSubmitterName.Text&lt;br /&gt;       oNewItem.Item("Submitter Email") = oSubmitterEmail.Text&lt;br /&gt;       oNewItem.Item("Message") = oMessage.Text&lt;br /&gt;       oNewItem.Item("Contact Email") = lblEmailAddressValue.Text&lt;br /&gt;&lt;br /&gt;       'update the list with the new item&lt;br /&gt;       oNewItem.Update()&lt;br /&gt;&lt;br /&gt;       'email the message to the designated contact&lt;br /&gt;       EmailMessage()&lt;br /&gt;&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       Dim lblErrorMessage As New Label&lt;br /&gt;       lblErrorMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;       lblErrorMessage.Text = "Error: " &amp;amp; ex.Message &amp;amp; _&lt;br /&gt;                 "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Details: " &amp;amp; ex.ToString&lt;br /&gt;       Controls.Add(lblErrorMessage)&lt;br /&gt;&lt;br /&gt;   End Try&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Sub EmailMessage()&lt;br /&gt;   'PURPOSE: email the contact that was selected&lt;br /&gt;&lt;br /&gt;   Dim strFromName As String = oSubmitterName.Text&lt;br /&gt;   Dim strFromEmail As String = oSubmitterEmail.Text&lt;br /&gt;   Dim strSubject As String = oMessageSubject.Text&lt;br /&gt;   Dim strMessage As String = oMessage.Text&lt;br /&gt;   Dim strToName As String = lblContactNameValue.Text&lt;br /&gt;   Dim strToEmail As String = lblEmailAddressValue.Text&lt;br /&gt;&lt;br /&gt;   Try&lt;br /&gt;       'build from email address and to email address&lt;br /&gt;       Dim FromEmailAddress As New Net.Mail.MailAddress(strFromEmail, strFromName)&lt;br /&gt;       Dim ToEmailAddress As New Net.Mail.MailAddress(strToEmail, strToName)&lt;br /&gt;&lt;br /&gt;       'create email and assign its subject and body&lt;br /&gt;       Dim MailMsg As New MailMessage(FromEmailAddress, ToEmailAddress)&lt;br /&gt;       MailMsg.Subject = strSubject&lt;br /&gt;       MailMsg.Body = strMessage&lt;br /&gt;&lt;br /&gt;       'create smtp email object&lt;br /&gt;       Dim smtpmail As New SmtpClient&lt;br /&gt;&lt;br /&gt;       'set email's smtp server&lt;br /&gt;       smtpmail.Host = SMTPMailHost&lt;br /&gt;&lt;br /&gt;       'send email&lt;br /&gt;       smtpmail.Send(MailMsg)&lt;br /&gt;&lt;br /&gt;       'clear form&lt;br /&gt;       ClearForm()&lt;br /&gt;&lt;br /&gt;       'display success message&lt;br /&gt;       lblStatusMessage.Text = "Message Sent Successfully!"&lt;br /&gt;&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       Dim lblErrorMessage As New Label&lt;br /&gt;       lblErrorMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;       lblErrorMessage.Text = "Error: " &amp;amp; ex.Message &amp;amp; _&lt;br /&gt;                 "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Details: " &amp;amp; ex.ToString&lt;br /&gt;       Controls.Add(lblErrorMessage)&lt;br /&gt;&lt;br /&gt;   End Try&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Sub ClearForm()&lt;br /&gt;   'PURPOSE: clear form of all values&lt;br /&gt;&lt;br /&gt;   'clear selected contact person's info&lt;br /&gt;   lblContactNameValue.Text = ""&lt;br /&gt;   lblJobTitleValue.Text = ""&lt;br /&gt;   lblEmailAddressValue.Text = ""&lt;br /&gt;   lblPhoneValue.Text = ""&lt;br /&gt;   lblFaxValue.Text = ""&lt;br /&gt;&lt;br /&gt;   'clear form fields&lt;br /&gt;   oMessageSubject.Text = ""&lt;br /&gt;   oSubmitterName.Text = ""&lt;br /&gt;   oSubmitterEmail.Text = ""&lt;br /&gt;   oMessage.Text = ""&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Function EmailContactDestinationIsValid() As Boolean&lt;br /&gt;   'PURPOSE: returns true if specified email list is valid for the web part&lt;br /&gt;&lt;br /&gt;   Try&lt;br /&gt;       'get the site the web part is running on&lt;br /&gt;       Dim oWeb As SPWeb&lt;br /&gt;       oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;       'get the list specified by user in property, if list is not found in site, flow will hit Catch&lt;br /&gt;       Dim oList As SPList&lt;br /&gt;       oList = oWeb.Lists(EmailContactDestination)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;       'check if fields exist in list&lt;br /&gt;&lt;br /&gt;       Dim blnMessageSubjectFound As Boolean = False&lt;br /&gt;       Dim blnSubmitterName As Boolean = False&lt;br /&gt;       Dim blnSubmitterEmail As Boolean = False&lt;br /&gt;       Dim blnMessage As Boolean = False&lt;br /&gt;       Dim blnContactEmail As Boolean = False&lt;br /&gt;&lt;br /&gt;       'loop through the fields in the list and check to see if all the required fields are in the list&lt;br /&gt;       For Each oField As SPField In oList.Fields&lt;br /&gt;           If Not oField.Hidden And Not oField.ReadOnlyField And oField.Type &lt;&gt; SPFieldType.Attachments Then&lt;br /&gt;&lt;br /&gt;               If oField.Title = "Message Subject" Then&lt;br /&gt;                   blnMessageSubjectFound = True&lt;br /&gt;               End If&lt;br /&gt;&lt;br /&gt;               If oField.Title = "Submitter Name" Then&lt;br /&gt;                   blnSubmitterName = True&lt;br /&gt;               End If&lt;br /&gt;&lt;br /&gt;               If oField.Title = "Submitter Email" Then&lt;br /&gt;                   blnSubmitterEmail = True&lt;br /&gt;               End If&lt;br /&gt;&lt;br /&gt;               If oField.Title = "Message" Then&lt;br /&gt;                   blnMessage = True&lt;br /&gt;               End If&lt;br /&gt;&lt;br /&gt;               If oField.Title = "Contact Email" Then&lt;br /&gt;                   blnContactEmail = True&lt;br /&gt;               End If&lt;br /&gt;&lt;br /&gt;           End If&lt;br /&gt;       Next&lt;br /&gt;&lt;br /&gt;       'if all fields were found, return true&lt;br /&gt;       If blnMessageSubjectFound And blnSubmitterName And blnSubmitterEmail And blnMessage And blnContactEmail Then&lt;br /&gt;           Return True&lt;br /&gt;       Else&lt;br /&gt;           Return False&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       Return False&lt;br /&gt;&lt;br /&gt;   End Try&lt;br /&gt;&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Private Function SMTPMailHostIsValid() As Boolean&lt;br /&gt;   'PURPOSE: returns true if SMTP Mail Host is valid&lt;br /&gt;&lt;br /&gt;   Try&lt;br /&gt;       If SMTPMailHost &lt;&gt; "" Then&lt;br /&gt;           Return True&lt;br /&gt;       Else&lt;br /&gt;           Return False&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       Return False&lt;br /&gt;&lt;br /&gt;   End Try&lt;br /&gt;&lt;br /&gt;End Function&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;III. Finally, we deploy the web parts to the SharePoint site, add them to a page, configure them, connect them to each other, then test them!&lt;br /&gt;&lt;ol&gt;&lt;li&gt;  in Visual Studio 2008, hit F5 (this should build and deploy the web parts to your site)&lt;/li&gt;&lt;li&gt;in your SharePoint site, select the page you want the web parts on, then click Site Actions -&gt; Edit Page&lt;/li&gt;&lt;li&gt;click Add A Web Part&lt;/li&gt;&lt;li&gt;scroll down to the Miscellaneous section, select ContactList web part and EmailContact web part, then click Add&lt;/li&gt;&lt;li&gt;In the ContactList Web Part, click Edit -&gt; Modify Shared Web Part -&gt; Data, and in Contact List Data Source, type our custom contact list we created, Contact List and hit OK. You should see the people you entered into the list appear in the web part.&lt;/li&gt;&lt;li&gt;In the EmailContact Web Part, click Edit -&gt; Modify Shared Web Part -&gt; Data, and in Email Contact Destination, type our custom email list we created, Sent Emails and hit OK. Next, enter your SMTP Mail Host in the next textbox. If you don't have your server configured for this, just type anything as the validation function in the code just expects any value. If you put in something that isn't your host, the web part just won't be able to email. You can customize the validation code yourself to indicate what you expect the user to enter.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;click the Email Contact web part and click edit -&gt; Connections -&gt; Get Contact Consumer From -&gt; Contact List (you could also have set the connection up from the Contact List web part as well)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Click Exit Edit Mode&lt;/li&gt;&lt;li&gt;Test the connected web parts by clicking a person in the Contact List and watching them appear in the Email Contact web part! In the Email Contact web part, enter in values and hit Send. This will save the message in the Sent Emails list. If you set up the SMTP mail host properly and the selected person has a proper email address, then that person will also be emailed the message you entered.&lt;/li&gt;&lt;/ol&gt;So there you have it! Now, exactly what is happening? In the ContactList web part, we created properties that implemented the interface IContact. The ConnectionProvider function ProvideIContact is what returns the interface. Originally, all the properties are empty, so this function won't have any values. When a contact is clicked, the ContactSelected routine fires off and populates the properties, so now ProvideIContact can return something. Now, the EmailContact web part has a ConnectionConsumer routine GetIContact that gets the values from ProvideIContact. Essentially, when we created a connection between the two web parts on step 7 above, we created a bridge between them via ContactList's ProvideIContact and EmailContact's GetIContact. As a side note, there is an interface called IWebPartRow, and I believe there would be a way to use that instead of the individual properties I created in IContact, but I went with the properties in IContact because it seemed easier at the time. Well, I hope this worked well. If you followed along and it didn't work, let me know in case I forgot something. However, I am pretty sure this should work 100%!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8738915332067259184?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8738915332067259184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/how-to-create-connected-web-parts-part.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8738915332067259184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8738915332067259184'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/how-to-create-connected-web-parts-part.html' title='How to create connected web parts: Part 2'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8479660567458833668</id><published>2009-07-01T12:53:00.010-04:00</published><updated>2009-07-01T13:36:56.789-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web part'/><category scheme='http://www.blogger.com/atom/ns#' term='web part gallery'/><title type='text'>Getting a web part's title and description (in .webpart) to update in the SharePoint site's Web Part Gallery</title><content type='html'>Here's one of those fun times with SharePoint, you know, where something simple doesn't work and you have to do backflips to get it to do what you want it to do. Now, to be fair, SharePoint has come a really long way, but there's still some quirky stuff we run into from time to time.&lt;br /&gt;&lt;br /&gt;  I developed a web part in Visual Studio 2008 and was getting ready to bundle it up for deployment using WSPBuilder. I then realized that I had not changed its default title and description. This information is set in the web part's .webpart file. In my case, its in AddContactFromAD.webpart. Upon deployment, you can see this information in the Web Part Gallery (Site Actions -&gt; Site Settings -&gt; Web Parts (under Galleries). If you click the Edit icon next to your web part, you see the Title and Description fields containing what was set in the .webpart file. When a user goes to add a web part to a page (Site Actions -&gt; Edit Page -&gt; click Add a Web Part), they see this title and description.&lt;br /&gt;&lt;br /&gt;  So I thought, no biggie, I'll just update the title and description in the .webpart file and redeploy and everything should be updated like usual, right? Wrong! I went back to the Web Part Gallery and the title and description hasn't changed a bit.&lt;br /&gt;&lt;br /&gt;  So an hour or two later, and after coming across another issue while trying to solve this issue, I finally came up with the solution.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;In Visual Studio, make your .webpart file's title and description the way you want them&lt;/li&gt;&lt;li&gt;Delete the web part from the Web Part Gallery  -   a. Click Site Actions -&gt; Site Settings -&gt; Web Parts (may need to click "Go to top level site settings" for Web Parts to show up;   b. Click the Edit icon of the web part;   c. Click Delete Item&lt;/li&gt;&lt;li&gt;Deactivate the web part  -  a. In Site Actions -&gt; Site Settings -&gt; Site Collection Features;   b. click Deactivate next to web part&lt;/li&gt;&lt;li&gt;In Central Administration -&gt; Operations -&gt; Solution Management, click web part's solution .wsp file, click Retract Solution and OK, and after its Status has changed to Not Deployed, click the .wsp file again and click Remove Solution&lt;/li&gt;&lt;li&gt;Close out of Visual Studio. This was the other issue I referred to above: at this point, if you attempt to deploy the web part without closing out Visual Studio and reloading it, you will get the error "The language-neutral solution package was not found"&lt;/li&gt;&lt;li&gt;After reloading Visual Studio, redeploy the web part!&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8479660567458833668?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8479660567458833668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/getting-web-parts-title-and-description.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8479660567458833668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8479660567458833668'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/07/getting-web-parts-title-and-description.html' title='Getting a web part&apos;s title and description (in .webpart) to update in the SharePoint site&apos;s Web Part Gallery'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-1728730231293970600</id><published>2009-06-23T21:25:00.056-04:00</published><updated>2010-06-09T23:58:31.938-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web part custom property'/><category scheme='http://www.blogger.com/atom/ns#' term='web part'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='list template'/><title type='text'>How to set a SharePoint web part's list at run time</title><content type='html'>Let's say you have a web part that must have a particular custom list you created in order for the web part to function. For instance, in the web part's CreateChildControls() routine, maybe you have some code similar to this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;'get the site the web part is running on&lt;br /&gt;Dim oWeb As SPWeb&lt;br /&gt;oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;'get the list we are going to work with&lt;br /&gt;Dim oList As SPList&lt;br /&gt;oList = oWeb.Lists("Contact List")&lt;br /&gt;&lt;br /&gt;...other code that loops through oList and displays the different field values...&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;So in the above example, the required custom list is called "Contact List". You may run into a few problems with this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;What if the required list doesn't exist?&lt;/li&gt;&lt;li&gt;What if the list exists, but it doesn't have the fields needed by the web part?&lt;/li&gt;&lt;li&gt;Every time you drop the web part on a page, it must have that list when it might be beneficial to have some flexibility with the list the web part is pointing to. A good example of this would be a site with a Marketing page and a Sales page. On the Marketing page, you want to display Marketing Contacts and on the Sales page you want to display Sales Contacts. Marketing Contacts and Sales Contacts are two separate lists.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;My solution to this has a few steps.&lt;br /&gt;&lt;br /&gt;First off, we need to create a custom property for your Contact List web part. Below is the code for the custom property.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;Dim strContactListDataSource As String = ""&lt;br /&gt;&lt;br /&gt;&amp;lt;WebBrowsable(True), Personalizable(True), FriendlyName("Contact List Data Source")&amp;gt; _&lt;br /&gt;Property ContactListDataSource() As String&lt;br /&gt;   Get&lt;br /&gt;      Return strContactListDataSource&lt;br /&gt;   End Get&lt;br /&gt;   Set(ByVal value As String)&lt;br /&gt;      strContactListDataSource = value&lt;br /&gt;   End Set&lt;br /&gt;End Property&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;So, the above code will add a new property called Contact List Data Source to the web part's properties you see when you click the web part in Edit Mode and click Modify Shared Web Part. Scroll down to the section Miscellaneous and you will see it. (Currently it is simply a textbox but it is possible to make it a dropdown list that lists available lists on the site).&lt;br /&gt;&lt;br /&gt;Now, if we go back to the first code snippet above that gets the list, we just change it to reference our new property:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;'get the site the web part is running on&lt;br /&gt;Dim oWeb As SPWeb&lt;br /&gt;oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;'get the list we are going to work with&lt;br /&gt;Dim oList As SPList&lt;br /&gt;oList = oWeb.Lists(ContactListDataSource)&lt;br /&gt;&lt;br /&gt;...other code that loops through oList and displays the different field values...&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;While this makes my web part dynamic in terms of what list to look at, I still have a problem. What if the user just added the web part to a page? The property is going to be empty initially until the user sets it. So what does the user see? Or, what if the list the user specifies in the property does not exist or is an invalid list?&lt;br /&gt;&lt;br /&gt;So, the next thing I do is create a function called ContactListIsValid() which will test to make sure the ContactListDataSource property the user specified is valid. If it is, the function will return true and the above code snippet will fire. If it is not valid, the function will return false and code will fire off that displays a message to the user telling them they need to set the ContactListDataSource property to a valid list.&lt;br /&gt;&lt;br /&gt;First, you will see ContactListIsValid(), then below that I will show you how I use it for displaying error messages. Note that this function verifies the list exists and has the required fields.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;Private Function ContactListIsValid() As Boolean&lt;br /&gt;'PURPOSE: returns true if specified contact list is valid for the web part&lt;br /&gt;&lt;br /&gt;Try&lt;br /&gt;'get the site the web part is running on&lt;br /&gt;Dim oWeb As SPWeb&lt;br /&gt;oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;'get the list specified by user in property, if list is not found in site, flow will hit Catch&lt;br /&gt;Dim oList As SPList&lt;br /&gt;oList = oWeb.Lists(ContactListDataSource)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;'check if fields exist in list&lt;br /&gt;&lt;br /&gt;Dim blnContactNameFound As Boolean = False&lt;br /&gt;Dim blnJobTitleFound As Boolean = False&lt;br /&gt;Dim blnEmailAddressFound As Boolean = False&lt;br /&gt;Dim blnPhoneFound As Boolean = False&lt;br /&gt;Dim blnFaxFound As Boolean = False&lt;br /&gt;&lt;br /&gt;'loop through the fields in the list and check to see if all the required fields are in the list&lt;br /&gt;For Each oField As SPField In oList.Fields&lt;br /&gt;   If Not oField.Hidden And Not oField.ReadOnlyField And oField.Type &lt;&gt; SPFieldType.Attachments Then&lt;br /&gt;&lt;br /&gt;       If oField.Title = "Contact Name" Then&lt;br /&gt;           blnContactNameFound = True&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If oField.Title = "Job Title" Then&lt;br /&gt;           blnJobTitleFound = True&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If oField.Title = "Email Address" Then&lt;br /&gt;           blnEmailAddressFound = True&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If oField.Title = "Phone" Then&lt;br /&gt;           blnPhoneFound = True&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;       If oField.Title = "Fax" Then&lt;br /&gt;           blnFaxFound = True&lt;br /&gt;       End If&lt;br /&gt;&lt;br /&gt;   End If&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;'if all fields were found, return true&lt;br /&gt;If blnContactNameFound And blnJobTitleFound And blnEmailAddressFound And blnPhoneFound And blnFaxFound Then&lt;br /&gt;   Return True&lt;br /&gt;Else&lt;br /&gt;   Return False&lt;br /&gt;End If&lt;br /&gt;&lt;br /&gt;Catch ex As Exception&lt;br /&gt;   Return False&lt;br /&gt;&lt;br /&gt;End Try&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;Finally, below is the code on how I use this in CreateChildControls().&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;'if ContactListSource is a valid list, display controls&lt;br /&gt;If ContactListIsValid() Then&lt;br /&gt;&lt;br /&gt;   Try&lt;br /&gt;      'get the site the web part is running on&lt;br /&gt;      Dim oWeb As SPWeb&lt;br /&gt;      oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;      'get the list we are going to work with&lt;br /&gt;      Dim oList As SPList&lt;br /&gt;      oList = oWeb.Lists(ContactListDataSource)&lt;br /&gt;&lt;br /&gt;      ...other code that loops through oList and displays the different field values...&lt;br /&gt;&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;      Dim lblErrorMessage As New Label&lt;br /&gt;      lblErrorMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;      lblErrorMessage.Text = "Error: " &amp;amp; ex.Message &amp;amp; _&lt;br /&gt;                "&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;" &amp;amp; "Details: " &amp;amp; ex.ToString&lt;br /&gt;      Controls.Add(lblErrorMessage)&lt;br /&gt;&lt;br /&gt;   End Try&lt;br /&gt;&lt;br /&gt;Else&lt;br /&gt;   'else display message telling user they must configure the ContactListDataSource property&lt;br /&gt;   Dim lblConfigureMessage As New Label&lt;br /&gt;   lblConfigureMessage.ForeColor = Drawing.Color.Red&lt;br /&gt;   lblConfigureMessage.Text = "&amp;lt;BR&amp;gt; &amp;lt;BR&amp;gt;" &amp;amp; "You must configure the Contact List Data Source property. " &amp;amp; _&lt;br /&gt;                          "&amp;lt;BR&amp;gt; &amp;lt;BR&amp;gt;" &amp;amp; "Please click Modify Shared Web Part -&gt; Miscellaneous -&gt; Contact List Data Source " &amp;amp; _&lt;br /&gt;                                       "and set it to a list based on the ContactListTemplate list template."&lt;br /&gt;   Controls.Add(lblConfigureMessage)&lt;br /&gt;End If&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;A final thought. Notice the error message tells the user to set the property to a list based on the ContactListTemplate list template. I created a list template from the Contact List custom list, so that way the user can create their contact list from the list template to make sure they have all the necessary fields. To do this, I went to the List Settings of Contact List and selected Save As List Template.&lt;br /&gt;&lt;br /&gt;Well, that's it! I wouldn't be surprised if people have their own way of doing all this, but I like to go for the simplest, most direct approach. I hope this helps!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-1728730231293970600?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/1728730231293970600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-set-sharepoint-web-parts-list-at.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1728730231293970600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/1728730231293970600'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-set-sharepoint-web-parts-list-at.html' title='How to set a SharePoint web part&apos;s list at run time'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-7183790146559133655</id><published>2009-06-17T18:42:00.033-04:00</published><updated>2009-07-14T01:52:19.645-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='connected web parts'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='provider'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='consumer'/><title type='text'>How to create connected web parts: Part 1</title><content type='html'>While this may be lengthy, my hope is that this is made as easy as possible. I begin this with a high level overview of connected web parts (Part 1), followed by a step-by-step tutorial on creating connected web parts where all you need to do is click along and paste code (&lt;a href="http://simplesharepointsolutions.blogspot.com/2009/07/how-to-create-connected-web-parts-part.html"&gt;Part 2&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Let's say you have two web parts: one that takes a zip code and displays the weather for that zip code, and another web part that takes a zip code and displays movies playing in movie theaters in or near that zip code. They both require a zip code textbox with appropriate validation. Plus, what if you need to develop another web part that also requires a zip code and then displays other information based on that zip code?&lt;br /&gt;&lt;br /&gt;Eliminate redundancy and welcome connected web parts! Instead of the above, we could make a zip code web part which acts as a provider, providing a zip code to another web part. Then our weather web part and movie web part could act as consumers, consuming a zip code fed to them by the zip code provider web part.&lt;br /&gt;&lt;br /&gt;Essentially, this involves creating a zip code interface that all three web parts will understand. Then we make each web part, specifying in the code that the zip code web part is a provider web part while specifying the weather and movies web parts are consumer web parts.&lt;br /&gt;&lt;br /&gt;Once the web parts and interface are built and deployed to a SharePoint site, a user can add the zip code web part to a page, add the weather web part to a page, then finally connect the two web parts via the Modify Shared Web Part option for either web part.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://simplesharepointsolutions.blogspot.com/2009/07/how-to-create-connected-web-parts-part.html"&gt;Part 2&lt;/a&gt; of this post is a tutorial on how to create Contact List/Email Contact connected web parts, along with entire code to be copy and pasted. I recommend following the tutorial to get the web parts functional, then look at the comments in the code to understand what everything is doing. Also, I will include a summary explaining the details of whats happening. Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-7183790146559133655?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/7183790146559133655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-create-connected-web-parts-part.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/7183790146559133655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/7183790146559133655'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-create-connected-web-parts-part.html' title='How to create connected web parts: Part 1'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-3858844844931205951</id><published>2009-06-16T13:57:00.009-04:00</published><updated>2009-06-18T11:17:45.553-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='requiredfieldvalidator'/><category scheme='http://www.blogger.com/atom/ns#' term='list library'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='connected web parts'/><category scheme='http://www.blogger.com/atom/ns#' term='regularexpressionvalidator'/><title type='text'>RequiredFieldValidator in connected web parts</title><content type='html'>I plan on posting a tutorial on how to easily create connected web parts, but I wanted to write a short post on a unique issue I just came across, along with a simple solution.&lt;br /&gt;&lt;br /&gt;Normally, when you add a RequiredFieldValidator to a form, it triggers when there is an attempted postback for that form. However, I have a scenario where I have two web parts, one called Contact List (this is the provider web part; it simply pulls data from a custom list library called Contact List and displays it) and another web part called Email Contact (this is the consumer webpart; it has a form where a user can enter their name, email, and message they want to email a contact). The Contact List web part provides a person while the Email Contact web part consumes a person. So when a user clicks a person in the Contact List, a postback is performed and the Email Contact web part will display that person's information above the form. A user could then enter their message, then click the Send button and it would email to the selected contact person.&lt;br /&gt;&lt;br /&gt;Ok, so did you notice the problem yet? If the Email Contact web part has required field validators on its form fields (ie required on the submitter's name, email, and message), then when a user clicks a person in the Contact List web part, the requiredfieldvalidators in the Email Contact web part will prevent the postback from occurring, thus the person is never sent from the Contact List web part to the Email Contact web part. (Note: this scenario assumed that Contact List and Email Contact are on the same web page in the same WebPartZone).&lt;br /&gt;&lt;br /&gt;The solution ended up being pretty simple. I am not going into the details of my specific implementation because the concept here is what it important (plus, I will post the code for this web part anyway in my article on connected web parts that I will do very soon). The solution is to use the ValidationGroup property of the RequiredFieldValidators and the Send button that saves/sends the message. To get this to work, I did the following:&lt;br /&gt;&lt;br /&gt;In the Email Contact web part (all this is done in CreateChildControls):&lt;br /&gt;1. create the RequiredFieldValidator controls for each field that needs it&lt;br /&gt;2. add the RequiredFieldValidator controls to the web part's Controls collection&lt;br /&gt;3. set their properties like ErrorMessage and ControlToValidate as you normally would&lt;br /&gt;4. set their ValidationGroup property to "EmailContactValidationGroup"&lt;br /&gt;5. when you create the form's button that saves/sends the email, set the button's ValidationGroup property also to "EmailContactValidationGroup"&lt;br /&gt;&lt;br /&gt;The key here is to make sure the ValidationGroup is the same for the button and validators. This way, the validators will only fire off when the button is pressed!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-3858844844931205951?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/3858844844931205951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/requiredfieldvalidator-in-connected-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/3858844844931205951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/3858844844931205951'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/requiredfieldvalidator-in-connected-web.html' title='RequiredFieldValidator in connected web parts'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-8549443884565399940</id><published>2009-06-13T20:11:00.024-04:00</published><updated>2009-07-17T14:35:40.960-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PeopleEditor'/><category scheme='http://www.blogger.com/atom/ns#' term='entity'/><category scheme='http://www.blogger.com/atom/ns#' term='web part'/><category scheme='http://www.blogger.com/atom/ns#' term='list library'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint'/><category scheme='http://www.blogger.com/atom/ns#' term='contact list'/><category scheme='http://www.blogger.com/atom/ns#' term='PickerEntity'/><title type='text'>How to populate a custom Contact List from AD</title><content type='html'>In a recent project at work, I was required to create a custom Contact List. The only problem was that I didn't want my SharePoint admins to have to go to the list, then click New -&gt; Item and have to sit there and enter in the person's name, email address, title, etc, when all this information is already sitting over in AD (Active Directory) to begin with.&lt;br /&gt;&lt;br /&gt;So, I wanted to provide my SharePoint site admins with the ability to search AD, select a person (or people), then with the click of a button, add them into my Contact List, which is a custom list library. My solution ended up being quite straightforward - create a web part with two controls, 1. a PeopleEditor control (the standard SharePoint control for selecting people from AD; it has those two little buttons in the lower right for checking the name you enter and a browse button to let you do searches) and 2. a Button control which will take whatever people have been entered into the PeopleEditor and save them into the Contact List.&lt;br /&gt;&lt;br /&gt;First off, this assumes you have created a custom list called "Contact List" on your SharePoint site. The fields are all text fields, called "Name", "Job Title", and "Email Address".&lt;br /&gt;&lt;br /&gt;1. Load Visual Studio 2008&lt;br /&gt;2. Click File -&gt; New -&gt; Project... -&gt; Visual Basic -&gt; SharePoint -&gt; Web Part&lt;br /&gt;3. For the name, I called mine AddContactFromAD&lt;br /&gt;4. Notice the web part was named WebPart1. Do a search and replace across the whole project and change WebPart1 to AddContactFromAD wherever it is found. While that just changed the contents of the files in the project, you will notice there is still a folder called WebPart1 with files inside all named WebPart1 with different file extensions. Manually rename the folder and the base names of those files (leaving the extensions alone for each).&lt;br /&gt;5. Copy and paste the code below.&lt;br /&gt;6. In the Solution Explorer, right click the project and click Properties, click the Debug tab on the left, then in "Start browser with URL:" type in the name of the SharePoint site you want to deploy the web part to.&lt;br /&gt;7. Hit F5 to build and deploy the web part.&lt;br /&gt;&lt;br /&gt;Code for AddContactFromAD.vb:&lt;br /&gt;&lt;br /&gt;1. under your inherits statement and above your constructor, copy and paste:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;WithEvents btnAddUsers As New Button&lt;br /&gt;Dim peADLookUp As New PeopleEditor&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;2. in CreateChildControls(), copy and paste:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;'create and add message to web part&lt;br /&gt;Dim lblMessage As New Label&lt;br /&gt;lblMessage.Text = "Add contact(s) from Active Directory into Contact List"&lt;br /&gt;Controls.Add(lblMessage)&lt;br /&gt;&lt;br /&gt;'add PeopleEditor control to web part (it was created above)&lt;br /&gt;peADLookUp.Rows = 1&lt;br /&gt;Controls.Add(peADLookUp)&lt;br /&gt;&lt;br /&gt;'add button to web part (it was created above)&lt;br /&gt;btnAddUsers.Text = "Add user(s) to Contact List"&lt;br /&gt;Controls.Add(btnAddUsers)&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;3. below CreateChildControls(), copy and paste the following subroutine.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;div style="font-family: courier new; background-color: rgb(255, 213, 149);"&gt;Sub btnAddUsers_Click() Handles btnAddUsers.Click&lt;br /&gt;'PURPOSE: add selected users to Contact List&lt;br /&gt;&lt;br /&gt;'get the site the web part is running on&lt;br /&gt;Dim oWeb As SPWeb&lt;br /&gt;oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)&lt;br /&gt;&lt;br /&gt;'get the list where we want to save the person to&lt;br /&gt;Dim oList As SPList&lt;br /&gt;oList = oWeb.Lists("Contact List")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dim strDisplayName As String = ""&lt;br /&gt;Dim strEmail As String = ""&lt;br /&gt;Dim strTitle As String = ""&lt;br /&gt;&lt;br /&gt;'loop through each entity in peADLookUp (note DisplayName,&lt;br /&gt;'   Email, and Title are all properties in AD)&lt;br /&gt;For Each account As PickerEntity In peADLookUp.ResolvedEntities&lt;br /&gt;   'get values of  current entity&lt;br /&gt;   strDisplayName = account.EntityData("DisplayName").ToString()&lt;br /&gt;   strEmail = account.EntityData("Email").ToString()&lt;br /&gt;   strTitle = account.EntityData("Title").ToString()&lt;br /&gt;&lt;br /&gt;   'create a new list item in Contact List&lt;br /&gt;   Dim oNewItem As SPListItem&lt;br /&gt;   oNewItem = oList.Items.Add()&lt;br /&gt;&lt;br /&gt;   'populate the new list item with values from the form&lt;br /&gt;   oNewItem.Item("Name") = strDisplayName&lt;br /&gt;   oNewItem.Item("Job Title") = strTitle&lt;br /&gt;   oNewItem.Item("Email Address") = strEmail&lt;br /&gt;&lt;br /&gt;   'update the list with the new item&lt;br /&gt;   oNewItem.Update()&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-8549443884565399940?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/8549443884565399940/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-populate-custom-contact-list.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8549443884565399940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/8549443884565399940'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/how-to-populate-custom-contact-list.html' title='How to populate a custom Contact List from AD'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-55538164283100985.post-2720980276333502165</id><published>2009-06-13T20:00:00.000-04:00</published><updated>2009-07-07T22:11:14.532-04:00</updated><title type='text'>Welcome to Simple SharePoint Solutions!</title><content type='html'>Welcome to Simple SharePoint Solutions! The point of this site is the sharing of simple, easy to understand SharePoint solutions. Despite the fact that SharePoint has all kinds of wonderful built in functionality, we just about always have unique requirements that are not out of the box. In steps us SharePoint developers, but sadly, there are a lot of posts out there that are not as straightforward as most of us would like. So this site is dedicated to all of us who just want a simple, easy to understand solution to our SharePoint problems! I am actively working on several SharePoint projects and will be posting many more how-to's very soon, so please check in and give some feedback on how helpful these articles are.&lt;br /&gt;&lt;br /&gt;D-J&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/55538164283100985-2720980276333502165?l=simplesharepointsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simplesharepointsolutions.blogspot.com/feeds/2720980276333502165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/welcome-to-simplesharepointsolutionsblo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/2720980276333502165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/55538164283100985/posts/default/2720980276333502165'/><link rel='alternate' type='text/html' href='http://simplesharepointsolutions.blogspot.com/2009/06/welcome-to-simplesharepointsolutionsblo.html' title='Welcome to Simple SharePoint Solutions!'/><author><name>D-J</name><uri>http://www.blogger.com/profile/11298184355113853198</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
