Monday, March 29, 2010

Creating a Multiple selection List Box in browser enabled InfoPath form

InfoPath 2007 browser enabled forms (a part of InfoPath Form services) does not support Multi-select List Box control. Read some other InfoPath Form services limitations here: http://office.microsoft.com/en-us/infopath/HA102040851033.aspx

I had a tough time explaining and convincing clients about this and they got another excuse of blaming it all on Microsoft, so I finally decided to solve this problem on my own.

The overall solution looks like this:
a. Create a  custom Data structure in InfoPath 2007 browser enabled form.
b. Bind a Repeating Table with our custom Data Structure and tweak the look and feel to make it look more like a Multi-select List box.
c. Use Form Load event for programmatically loading Multi-Select List options.
d. On Submit button click, Save the multi-selected List box items as a semi-colon separated string in another InfoPath text field.

I will explain the above solution Step-by-Step with snapshots to make it more easier to implement.

a. Create a Data Structure in InfoPath 2007: See the snapshot below:

i) In this case, my field name for storing the values selected in the multi-select List items is WorkcenterAssignment. You can give any name you want to the Group and the field. Values selected by the user will be stored in this field.

ii) For MultiSelectOptions, follow the same structure as mentioned below:
-MultiSelectOptions(Repeating Group) - Will be applied as a repeating table in Step b.
    - selectedOption (True/False Boolean field) - This will be converted into Checkbox on the UI
    - optionDescription (Text string field) - This will be used for storing the multi-select List Item options

b. Bind a Repeating Table with the Data Structure
i) Right click on the above schema -> Choose Repeating table as follows:

ii) It will look like this on the InfoPath form:
iii) Format it to look more like a multi-select ListBox on your form:
Note: You can also wrap it inside a Table with a fixed width and fixed height, so that Listbox items can act like scroll bar for options)

c. Programmatically loading Multiple items in the List Box from SharePoint List/Library:
Now that our schema and control are all set, we need to load values into multiselect Listbox control.
The key event here is the InfoPath form load event. 

The strategy here is to bind the repeating table created above programmatically using Visual Studio for Applications. I prefer to write the code in C# .NET.

Here is my Form Load event, I call the generic method LoadMultiSelectListBox() method, copy the code below:

 public void FormEvents_Loading(object sender, LoadingEventArgs e)
        {
            try
            {
                this.Errors.DeleteAll();

                    XPathNavigator root = MainDataSource.CreateNavigator();
                    XPathNodeIterator listItems = root.Select("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions", NamespaceManager);

                    if (listItems.Count == 1)
                    {
                        //Load Multi-Select List Box
                        LoadMultiSelectListBox();
                    } 
            }
            catch (Exception exp)
            {
                ThrowException(exp);
            }
        }



Here is the code for Loading Multi-Select items into the repeating table programmatically. I first need to get all my List item options from my SharePoint List/Library, I have created a XML Receive Data connection in InfoPath, you can alternatively get all the options from SharePoint List/Library using the SharePoint object model, copy code below:

        private void LoadMultiSelectListBox()
        {
            try
            {
                this.Errors.DeleteAll();

                //Query Get Approvers From SharePoint List data connection
                FileQueryConnection queryConnection = (FileQueryConnection)this.DataConnections["WorkCenterXML"];
                queryConnection.Execute();

                //Get all nodes from the Work Center XML
                XPathNavigator connectionNav = DataSources["WorkCenterXML"].CreateNavigator().SelectSingleNode("//rs:data", this.NamespaceManager);
                XPathNodeIterator allItems = connectionNav.Select("//z:row", this.NamespaceManager);

                //Iterate via all records
                foreach (XPathNavigator item in allItems)
                {
                    AddMultiSelectListItems(item);
                }

                //Delete the first row of InfoPath Repeating table programmatically
                if (GetCurrentXPathNav("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions[1]") != null)
                {
                    GetCurrentXPathNav("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions[1]").DeleteSelf();
                }
            }
            catch (Exception exp)
            {
                ThrowException(exp);
            }
        }

This method adds values into our custom repeating table data structure:

        private void AddMultiSelectListItems(XPathNavigator item)
        {
            try
            {
                this.Errors.DeleteAll();

                //Get Reference to Multi-Select List Options
                XPathNavigator Item = GetCurrentXPathNav("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions");

                //Create a new Item node for Multi-Select List items
                XPathNavigator newItemNode = null;

                if (Item != null)
                {
                    //Clones the new item w.r.t repeating table structure
                    newItemNode = Item.Clone();
                }

                //Get Reference to Option description
                XPathNavigator optionDescription = newItemNode.SelectSingleNode("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions/my:optionDescription", this.NamespaceManager);

                //Set Option description values
                optionDescription.SetValue(item.GetAttribute("ows_Workcenter", String.Empty));

                //Add a new Item in the Multi-Select List options
                Item.InsertAfter(newItemNode);

                optionDescription = null;
                newItemNode = null;
            }
            catch (Exception exp)
            {
                ThrowException(exp);
            }
        }

Updated as on 5th April, 2010: GetCurrentXPathNav is a generic method used to get the handler on the particular node/field in InfoPath, copy the code below:

///
/// Get XPath Navigator gets the Navigator object for the passed in XPath expression.
/// This method should be used as a helper function at all times
///
/// XPathNavigator
public XPathNavigator GetCurrentXPathNav(String XPath)
{
    XPathNavigator DataSource = MainDataSource.CreateNavigator();
    XPathNavigator XPathNav = DataSource.SelectSingleNode(XPath, NamespaceManager);
    return XPathNav;
}

Preview your InfoPath form, your control should load all the options dynamically and look like this:

d. Saving the values selected from the Multi-select List box item on saving the form:
Assume user selects multiple items, we will have to save the values selected in our Multi-select Listbox control using a separate field in the InfoPath form, in this example, we created the WorkcenterAssignment text field in Step a.

Again we will use the programming approach to iterate through the repeating table nodes, identify the list items selected and store them as semi-colon (;) separated values in the WorkcenterAssignment InfoPath field.

WorkcenterAssignment can be further promoted as a SharePoint column using Property Promotion feature in the InfoPath form. To know more on publishing and deployment of InfoPath forms, see my detailed post on Publishing and Deploying browser based InfoPath forms


Here is the code to Save the selected Multi-select Listbox items:

private void SaveMultiSelectListItems()
        {
            try
            {
                this.Errors.DeleteAll();

                XPathNavigator root = MainDataSource.CreateNavigator();
                XPathNodeIterator listItems = root.Select("/my:Request/my:WorkCenterAssignments/my:MultiSelectOptions", NamespaceManager);

                String optionsSelected = String.Empty;
                StringBuilder selectedListItems = new StringBuilder(String.Empty);

                int counter = 0;

                while (listItems.MoveNext())
                {
                    optionsSelected = listItems.Current.SelectSingleNode("my:selectedOption", NamespaceManager).Value;

                    //Check whether the Check box against the option was selected or not
                    if (Boolean.Parse(optionsSelected) == true)
                    {
                        if (counter == 0)
                        {
                            selectedListItems.Append(listItems.Current.SelectSingleNode("my:optionDescription", NamespaceManager).Value);
                        }
                        else
                        {
                            selectedListItems.Append("; " + listItems.Current.SelectSingleNode("my:optionDescription", NamespaceManager).Value);
                        }

                        //increment the counter
                        counter++;
                    }

                    //Clear variable
                    optionsSelected = String.Empty;
                }

                //Set all the multi-selected List Box values
                GetCurrentXPathNav("/my:Request/my:WorkCenterAssignments/my:WorkcenterAssignment").SetValue(selectedListItems.ToString());
            }
            catch (Exception exp)
            {
                ThrowException(exp);
            }
        }


I think the above code is self-explanatory. This is how we simulate a Multi-select List control using the repeating table model and dynamic options loading in a browser enabled InfoPath form.

Feel free to share your comments.


References: InfoPath Team blog

Sunday, March 7, 2010

Enable Forms Based Authentication (FBA) in SharePoint 2010

Upcoming Posts: Have not been regular with my blogging in the last month, did lots of R&D on the InfoPath 2007 browser enabled forms and Form services, my future posts will be in the following areas:

A) Multi-select List Items in a browser enabled InfoPath form, which was earlier thought not to be possible with Form Services, stay tuned for that post.

B) Cascading dropdowns using code-behind InfoPath solution and owssvr.dll for dynamic filtering within the dropdowns using XML.

Coming back to Forms Based Authentication, here is a nice step-by-step tutorial, I found on Enabling Forms Based Authentication in SharePoint 2010: http://blog.summitcloud.com/2009/11/forms-based-authentication-sharepoint-2010-fb/

For enabling FBA on SharePoint 2007, refer:  http://blog.summitcloud.com/2009/10/enable-forms-based-authentication-for-sharepoint/

Stay tuned for my interesting hacks on InfoPath 2007. Till then Happy Programming :)

Thursday, January 14, 2010

Nintex My Workflow tasks webpart - Redirect to Custom InfoPath Task form

My earlier post related to a unique problem in Nintex OOTB "My Workflow Tasks" webpart. The job of this webpart is to show all Workflow Tasks assigned to the currently logged in user.

The problem occurs when InfoPath forms come into picture, since we might have a custom InfoPath task form, and the default behavior of the webpart directs the user to the Nintex OOTB task form, so it is has no provision for redirecting users to a Custom InfoPath Task form.

There are 2 possible ways to overcome this limitation:

Approach 1: in this approach I create a new custom InfoPathApproveReject.aspx form which overrides the OOTB Nintex ApproveReject.aspx Task form and hides certain section of the form like the Approve/Reject radio buttons. Check out Approach 1 here
 
Approach 2: Is modifying the existing Nintex OOTB ApproveReject.aspx task form in the 12 Hive layouts directory. The approach is to identify whether the ItemProperties contain .xml as an extension or not, so we use the following code in the ApproveReject.aspx page:

 if (this.itemProperties.Item.Name.ToString().Contains(".xml"))
   {
          string redirectUrl = this.itemProperties.Item.Web.Url + "/_layouts/FormServer.aspx?XmlLocation=" + this.itemProperties.Item.Web.Url + "/" + this.itemProperties.Item.Url.ToString() + "&OpenIn=browser";
          Response.Redirect(redirectUrl);
   }

This will work for all web applications in your SharePoint environment. If it is a custom InfoPath form in the Item Properties object, then it simply redirects the logged in user to the Custom InfoPath task form, otherwise it shows up only the OOTB Nintex task form.

Click here to download the customized version of Nintex ApproveReject.aspx form.  

NOTE: Take a back up of your existing copy of OOTB ApproveReject.aspx page from the following location C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\NINTEXWORKFLOW.

Then replace the customized version of ApproveReject.aspx page in the above mentioned location.

Test your changes:
To test the new change, you need to have a workflow enabled List/Library with a custom infopath form and Nintex OOTB My Workflow Tasks webpart on any of your site page.

Now when the task gets created it shows up in the webpart and when you click on the Item it opens up the InfoPath custom task form.

However, if you have a standard workflow on a SharePoint list/libray without InfoPath form, then you need not worry, since it would automatically open up the OOTB Nintex task form as it never found .xml within the ItemProperties object.

Thursday, December 17, 2009

Deploy browser enabled InfoPath forms as SharePoint feature - InfoPath 2007 form and Nintex workflows 2007 - Part 2

InfoPath 2007 form and Nintex workflows 2007 Part 1 covered basics of integrating InfoPath forms with Nintex workflows using in-built Nintex webservices, creating Receive Data Connections, submit data connections, creating Form Load Rules and building complex InfoPath Role based Views.

InfoPath 2007 form and Nintex workflows 2007 - Part 2 covers on how to deploy browser enabled InfoPath forms as Content Types in your SharePoint List/Library.

For deployment, we need to follow the steps mentioned below:
a. Converting InfoPath Data Connections to DCL library in SharePoint.
b. Publishing InfoPath form to a SharePoint List/Library 
c. Creating a .wsp solution package for the InfoPath form and its code-behind
d. Creating a batch script that will deploy the InfoPath form on your Production site.
e. Ensuring the InfoPath form has been deployed as a feature
f.  Modify the DCL's in the production environment.
g. Associate the InfoPath Content Type with the Document/Forms Library
a. Convert all your InfoPath Data Connections into the Data Connections library (DCL) in SharePoint.
This is a very important step before deployment, because you need to modify these Data Connection URL's on the Production server. Suppose you have a Receive Data Connection from a SharePoint list in your InfoPath form, the List GUID and URL will be different for the Production environment, also your Submit data connection URL will be different for the Production environment. This is where DCL's come into picture.
Remember the web.config file in ASP.NET, where we store all our connection strings. Just before giving the msi build, we change the web.config connection settings to point to the correct production url without the need for re-compiling and re-building the project. Same applies to DCL, you cannot move ahead with your InfoPath form deployment without changing converting your existing InfoPath form data connections to a SharePoint DCL library.
NOTE: However, after deployment you have to change these DCLs in the production environment.
Snapshots below show how to convert your InfoPath form Data Connections to DCL's :
1. Create a new Data Connections Library in your Development environment:































2.. You can name it anything you want, for eg: DCL
3. DCL library just got created. This is the place where all your InfoPath Data Connections gets stored in the form of a .udcx file.
4. Go to your InfoPath Form Tools -> Data Connections, you will see all your data connections:
5. Select your InfoPath Data Connection and Click on the "Convert" button besides it. There are two options here: Relative to Site Collection and Centrally managed, choose Relative to Site Collection, because we will later export this site to production environment and there we need to get all these Data Connections.
Give an appropriate name to your data connection and Click on the Ok button
6.  Go to the DCL library, you will see the InfoPath connection appears herem check the status, it shows as Pending 
 7. Click on the Name Context menu, select Approve/Reject link
 8. Select the radio button Approved and click on Ok button.
 9. The DCL is now seperated from InfoPath form and has all the connection details.
 10. The Convert button against the converted Data Connection becomes disabled

































11. Once the DCL is created, you cannot re-convert the same InfoPath Data Connection again, so inorder to modify the same you need to recreate the Data Connection and then click on Convert again. Therefore, to avoid rework, it is always advisable to create data connections after test your InfoPath forms thoroughly in your Development environment before moving to test/production.
NOTE: An important thing to remember is, that the DCL still holds the connection URLs for the development environment in the form of .udcx, we need to change them, when we deploy the site to Production environment, if we already know the Production URL, then we can change it here itself, but sometimes there are Receive Connections like Receive From SharePoint List/Library, XML data connection that have the List GUID too. Since we cannot predetermine the List GUID's for production, it is always advisable to modify these DCL's in the production environment. 

b. Publish your InfoPath forms to a SharePoint List/Library: Once all the Data Connections have been converted to the DCL library in SharePoint, we need to Publish the InfoPath form.

1. Build and Re-build your InfoPath code behind solution first
































2. Open your Infopath form, Go to File -> Publish, see snapshot below

3. Follow the Steps in the Publishing Wizard, select first radio button i.e Publish to a SharePoint server and click on Next button
































4. Enter your Site URL and click on Next button































5. You may get this error at first, do not worry, it is due to the service Event Notification, which abnormally stops the Publishing
































6. Be sure to Stop the System Event Notification services, go to Start -> Run -> services.msc, select System Event Notification and stop the service.
































7. Now click on the Ok button of your error message pop-up and click the Next button, do not change anything here, click on Next button again































8.  Enter your Publishing Location, this is the path where you will save your Published Form (this form will be used for Deployment to SharePoint), do not miss out to name your Published Form here, for eg: MyPublishedForm.xsn or the same name as your existing Form Template. Click on Next button
































9. Promote the required InfoPath fields you want into the SharePoint, the promoted fields become a part of the Content Type that we associate in our Document Library/ Form Library, click on the Next button.
 

NOTE: You can even allow the InfoPath form fields to be editable in SharePoint, by selecting the checkbox

10. Click on the Publish button now.































11. After clicking on Publish button, you will see the following screen, click on Close to complete the publishing process.
































c. Create an InfoPath solution package for deployment: We are now ready to create a package of the above browser enabled InfoPath form.
Install the famous WSP builder for Console application from here (Download WSPBuilder.exe version 0.9.9.0728 x86 (Console app)).
1. In your hard disk drive, under the folder of your choice lets say Deployment, simulate the 12 hive structure
i.e create the following folders: 12, Tempalte, Features, Your Feature Name Folder
2. Next Download the sample feature.xml and element.xml files from the FeatureFiles.zip, you need to modify them, I have kept the relevant placeholders, change the values in the placeholders specified. I have scoped my InfoPath feature at the Site level. Next copy and paste your Published InfoPath form.xsn and InfoPath Form.dll within the features folder
3. Next create a solution package for the feature, download the Create WSP batch file sample, all you have to do here modify values in the placeholders I have specified like creating a new GUID and modifying the WSP Name, you may also have to change the WSP Builder path.
Place the createwsp.bat file in the same folder you have your 12 hive structure created, see the snapshot:
4. Run the createwsp.bat and it creates the feature solution package as shown in the snapshot below:
d. Create a batch script to Install your InfoPath forms:  Download the InfoPathInstall.bat file batch file, this batch file will be used to Install the InfoPath form feature, change your InfoPath form .wsp name in the batch file, you may also have to change your stsadm path. See snapshot below:
Remember, this batch file and wsp package will be helpfull to you in case of Production deployment, where you do not have much access due to IT Policies and restrictions, you just need to give the .wsp and InfoPath Install.bat to the Server Administrators and they will do the needfull, only thing you may need to do is to manually Activate the feature via the interface.
I have deliberately not included the activate feature command, since you may undergo a lot of changes in the InfoPath form package and you may have to do multiple re-deployments as well. It created a lot of issues for me while I was upgrading the package and re-deploying as it was making the new version of Content Type and not updating the existing Content Type. But when i only use Retract, Delete, Add and Deploy solution, it updates the Content Type properly. So i stick with this method, you are free to experiment by adding Install Feature, Activate Feature etc in your batch scripts.
e. Ensuring the InfoPath form has been deployed as a feature: We assume that you are in your development environment and have completed the above mentioned steps, so deploying the .wsp created above is the only pending task now. Run the InfoPath Install.bat file you created above in order to install your InfoPath form as a feature
NOTE: 2 Additonal deployment steps for Production environment:
=========================================================================
In case you are ready to move your existing site collection and InfoPath forms into the Production environment, please follow the procedure mentioned below:
 Before running the InfoPath Install.bat file in your Production envrionment, please ensure that:
1. You have exported your development site collection using:
stsadm -o export -url "http://yourwebappname/sites/sitecollname" -filename "C:\Deployment\ITPurchaseBackUp.bak"
2. You have already created a Blank site collection in your production/deployment environment and ran the stsadm import command with the help of the exported .cmp
stsadm -o import -url "http://yourwebappname/sites/sitecollname" -filename "C:\Deployment\ITPurchaseBackUp.bak"
=========================================================================
Since the InfoPath feature is globally deployed, it is available as a feature in all the site collections in my web application. You can choose to scope it only respective to your site collection by modifying the InfoPath Install.bat file, I leave it upto you.
The next step is to check whether our Published InfoPath form has been deployed as a feature or not:
Go to your Site Settings -> Site Collection Features, find your feature and manually Activate it, see snapshot below:


The moment you activate this feature Content Type specific to this solution gets created, Go to Site Settings -> Site Content Types -> Choose Microsoft Office InfoPath from the Show Group Dropdown

f. Modify the DCL's in the Production environment: I believe you would have deployed your InfoPath Form solution wsp package and activated the feature in your respective Site Collection by now.
In case you are in your development environment, the DCLs will automatically get created in your DCL library when you Convert your InfoPath data connections. We already completed that step. Now we want to move this site to the Production environment.
I have specified the 2 additional steps for Production environment deployment in the previous step above., please follow that first.
Once you have imported your site, ensure that you activate your InfoPath form feature by following the steps mentioned in the previous point above.
Login as a user with Owners rights and go to your imported site collections DCL library, it will still have the connections referring to the Development environment. Now its time to change those connections by downloading the DCLs into your local disk, modifying and uploading the DCLs back to the library.
Let us follow the steps mentioned below:
a. Download the DCLs: Go to your Production site's DCL library, right click on the Type icon and click on Save Target As...for a specified Data Connection






b. Save your .udcx file as an XML type file so that it is available for editing, you can either Save it in your Desktop or any other drive for that matter.
c. Right Click the downloaded .udcx file, Open With Notepad and modify the ListID and WebUrl attributes, these attributes vary with the type of Data connection, so in case of a Submit Data Connection you wont see any ListID attribute, instead there will be a FolderName attribute that points to the Document/Form Library path where the form will be submitted to. Modifying your DCL's is nothing more than common sense, so everything is not covered in this post.
The example we will cover is that of a Receive Data Connection type.





d. Once modified with correct Production List GUIDS and Url, you need to upload the updated DCL back to the DCL library, see snapshots:
Go to your DCL library -> Upload button
Browse and attach the modified .udcx file
Click on Ok button, do not uncheck the checkbox.
 
Ensure that the name and title should be the same with proper casing as it appears in the DCL library, do not change the other attributes, click on the Ok button
 
Select the Approve/Reject the modified and uploaded .udcx file and set it status to Approved.
 
g. Associate the InfoPath Content Type with the Document/Forms Library:
1. Go to your Form/Document library where you want to use the InfoPath form content type, go to Document Library Settings
 2. Select Advanced settings
 
3. Select the Radio button : Allow Management of Content Types to Yes and select Display as a web page option in Opening browser-enabled documents
4. Go to Document Library settings again and Add the Microsoft Office InfoPath Content Type that we deployed using Add from existing content type option in the document library.
 
5. Next go to your Document Library and click on New option, the new InfoPath Content Type will be visible with an InfoPath icon besides it. See snapshot
 Your InfoPath form should open up fine, if it throws errors like Cannot connect to the Data Source, then be sure to check your .udcx files in DCL's again.
With this we complete the 2nd part series which covers correct way of deploying your InfoPath forms to the Production environment.
Stay tuned for Part 3 which will cover Nintex workflow deployment, I will share my workflow deployment scripts and share tricks and tips to deploy your Nintex workflows the correct way.
In case you face any issues implementing the same, drop in your comments, I will be glad to help you...















Monday, December 7, 2009

Lotus Notes to SharePoint Application Migration Drill Down Estimation template

There are 4 parts to a typical Notes to SharePoint migration project, they are:
a. User account migration
b. Data migration
c. Application migration
d.  Mail Box migration

Out of all the above parts, Application migration requires a huge amount of skill and effort specially when you rely on Visual Studio's Windows Workflow Foundation (WWF) for your workflow development, on the other hand if you have Nintex workflows your job becomes much more easier.

I am heavily into Application migration one because it is challenging and secondly it helps you understand the nature of the business processes that different companies follow.

Based on my day-to-day experiences with Application migration, I have prepared a Drill Down Application migration estimation template which captures all the key parameters that will help you develop a near perfect estimate.

Click here to download the Lotus Notes to SharePoint Application Migration Drill Down Estimation template

Once the estimation is in place, half the battle is won and success is close by. But overall success of a migration project also depends heavily on technical skills that your team posses, effective planning, resource mobilization, risk management and client communication.

I personally believe team members and tech leads need to be given good solid technical trainings before they actually start a migration project. Especially when you use some third party tool for Data migration like Quest Notes Migrator for SharePoint or Nintex workflows for Application migration, Microsoft InfoPath for business process forms. These tools have their own strengths and limitations, understanding both will help you play in a more safer zone and avoid conflicts and mismatch in the requirements, because lets face it, everything that you can do with Notes cannot be done in the same way in SharePoint and the vice versa. So a healthy relationship with the client and effective communication will help you save tons of hours of rework and change requests.

Do let me know, in case you have any comments or feedback on the template