Saturday, September 25, 2010

InfoPath 2010: The Infamous Form Name

Yes the infamous form name issue. I rant on about this in my InfoPath Book. I always had an out with the dilemma of new versus updates and keeping the same name.



Several members of my audience at SharePoint Saturday Baltimore eluded to a good solution and one of my colleagues came up with the same idea. It's not anything new but a good altenative if the fields you decide to use can be edited and changed.


The first part of this requires a common theme in form template design; determining if the form is a new instance. I have used a IsNew-type field in the past where the default is 0 or false and when the form instance is rendered (or submitted), rules force it to 1 or true.


So for the form name, instead of using a function at the time of submission, use a field value that has been pre-calculated. If the form is new, create the form name and store that into a field. Then, use that field as the form name that is saved during submission.



This way, the form name will never change during edits. So using an entered field, todays date, etc. in the form name will never have the chance to change since the form name is initially figuired out when the form is new.



Monday, September 13, 2010

SP2010 Patches

The August 31, 2010 patches for SharePoint Foundation and SharePoint Server can be found at the following links:


http://support.microsoft.com/kb/2266423


http://support.microsoft.com/kb/2352342

Upgrade Strategies for SharePoint 2010

If you missed the Quest webcast on SharePoint 2010 migrations (featuring Joel Oleson) , I highly recommend viewing the recorded session which can be found here.

A few main points:

  • Testing, testing, testing! Even if you have gone through dozens of migrations from 2007 to 2010, each farm is different and you should apply the proper due diligence to test the migrations prior to performing the actual upgrade.
  • The MOSS 2007 farm must be SP2 with the latest CU paks with the CU from December being recommended.
  • SP2+ on MOSS 2007 makes SharePoint aware of the database in read-only mode. Therefore, you can make the content databases of your MOSS 2007 farm read-only and SharePoint will automatically hide any Edit, Add, or Delete operations from its menus.
  • Making your databases read-only is a good way to migrate the data since users cannot change anything while you are upgrading. However, the content databases that are being migrated cannot be read-only so you will need to make a copy before attempting DB-Attach operations.
  • The migration check in MOSS 2007 is the preupgradecheck in stsadm.
  • The migration check for SharePoint 2010 is the PowerShell command TEST-SPContentDatabase
  • In-Place migration methods seem to handle most farms, however, it is not recommended as if anything goes wrong, there is no roll back. Your MOSS 2007 farm will be down until all issues can be resolved.

InfoPath 2007/2010/2013: Populate a Repeating Table from a Secondary Data Source

Psst! I have a new nested solution posted here.

Background
In attempting to create a solution within form code to satisfy the scenerio/requirements below, I found several different posts/links that described certain functions. Each link only provided a piece of the puzzle. This post puts all the pieces together.


Scenario/Requirements
Clicking a button or changing a selection in an InfoPath form needs to query a web service (or any secondary data source) based on that selection and populate a repeating table with the data. If the selection changes, the repeating table entries need to be cleared and re-populated with the new set of data.


Assumptions
The querying of the secondary data source (such as a web service) based on selections is already in place (this can be easily done with rules). We just need the proper form code within the control change-event to handle the repeating table population.



Solution Piece-by-Piece

Namespace Variable
Within the changed event form code (using Visual Studio for Office Applications) the first thing we need is the standard namespace variable so we can use that throughout:

string myNamespace = NamespaceManager.LookupNamespace("my");

Access Web Service-Based Secondary Data Source
There is a MainDataSource object at the form level but how can you access secondary data sources? If the datasource is named "GetData" here is the code to access and setup the XPath objects for looping:

DataSource ds = DataSources["GetData"];
XPathNavigator domNav = ds.CreateNavigator();
XPathNodeIterator rows = domNav.Select("/dfs:myFields/dfs:dataFields/tns:GetDataResponse/tns:GetData/NewDataSet/DynamicData", NamespaceManager);

Looping Through the Secondary Data Source
You can loop through the XPathNodeIterator collection using a while-loop. The source fields data can be retrieved by using the Current pointer within the XPathNodeIterator:

while (rows.MoveNext())
{
string accountID = rows.Current.SelectSingleNode("AccountID",NamespaceManager).Value.ToString();
string accountNumber = rows.Current.SelectSingleNode("AccountNumber",NamespaceManager).Value.ToString();
string amount = rows.Current.SelectSingleNode("Amount",
NamespaceManager).Value.ToString
}

Populating the Repeating Table
The repeating table is actually part of the main data source so we can access that and use the XMLWriter to write the field values from the web service to the table. The code below will live within the loop from above:

using (XmlWriter writer = MainDataSource.CreateNavigator().SelectSingleNode("/my:MainDataSource/my:group1", NamespaceManager).AppendChild())
{


writer.WriteStartElement("group2", myNamespace);
writer.WriteElementString("Amount",myNamespace ,amount);
writer.WriteElementString("AccountID", myNamespace, accountID);

writer.WriteElementString("AccountNumber", myNamespace,
accountNumber);

writer.WriteEndElement();
writer.Close();
}

The above assumes the repeating table created a Group1\Group2 data source entry. You must look at the main datasource to determine the group names.

ALSO! VERY IMPORTANT! The order in which you write the values to the table should be the order that they appear in the main data source. So if you expand the group1 and group2 and see field1, field2, field3, that is the order you must have in the code above. Otherwise you will receive a non-datatype schema validation error.


Clearing Previous Entries
Before anything happens, we need to check if there are already rows in the repeating table and remove them. This must be done by removing the rows in a descending fashion because the count actually represents the highest index.

First we check to see if there are any rows in the repeating table and if so we loop through and delete them. Notice how the actual row is accessed using the SelectSingleNode (SelectSingleNode("/my:MainDataSource/my:group1/my:group2[row#]):

XPathNavigator rTable = MainDataSource.CreateNavigator();
XPathNodeIterator tableRows = rTable.Select("/my:MainDataSource/my:group1/my:group2", NamespaceManager);
if (tableRows.Count > 0)
{


for (int i = tableRows.Count;i > 0; i--)
{
XPathNavigator reTable =
MainDataSource.CreateNavigator();

XPathNavigator reTableRows =
reTable.SelectSingleNode("/my:MainDataSource/my:group1/my:group2[" + i + "]", NamespaceManager);

reTableRows.DeleteSelf();
}
}




Solution - Complete Code
//Get namespace
string myNamespace = NamespaceManager.LookupNamespace("my");
//Clear any previous entries
XPathNavigator rTable = MainDataSource.CreateNavigator(); XPathNodeIterator tableRows = rTable.Select("/my:MainDataSource/my:group1/my:group2", NamespaceManager);

if (tableRows.Count > 0)
{
for (int i = tableRows.Count;i > 0; i--)
{
XPathNavigator reTable =
MainDataSource.CreateNavigator();
XPathNavigator reTableRows =
reTable.SelectSingleNode("/my:MainDataSource/my:group1/my:group2[" + i + "]",
NamespaceManager);

reTableRows.DeleteSelf();
}
}
//Connect to secondary data source
DataSource ds = DataSources["GetData"];
XPathNavigator domNav = ds.CreateNavigator(); XPathNodeIterator rows = domNav.Select("/dfs:myFields/dfs:dataFields/tns:GetDataResponse/tns:GetData/NewDataSet/DynamicData", NamespaceManager);

//Loop through secondary data source and populate the repeating table

while (rows.MoveNext())
{

string accountID =
rows.Current.SelectSingleNode("AccountID", NamespaceManager).Value.ToString();

string accountNumber =
rows.Current.SelectSingleNode("AccountNumber",NamespaceManager).Value.ToString();

string amount =
rows.Current.SelectSingleNode("Amount", NamespaceManager).Value.ToString();

using (XmlWriter writer =
MainDataSource.CreateNavigator().SelectSingleNode("/my:MainDataSource/my:group1",
NamespaceManager).AppendChild())


{
writer.WriteStartElement("group2", myNamespace);
writer.WriteElementString("Amount",myNamespace ,amount);
writer.WriteElementString("AccountID",
myNamespace, accountID);

writer.WriteElementString("AccountNumber", myNamespace,
accountNumber);

writer.WriteEndElement();
writer.Close(); }

}


WHAT ABOUT A NESTED REPEATING TABLE? THE ANSWER IS HERE!

You can download a sample form and project code with the purchase of my bestselling InfoPath book:



The Nested Solution will be part of my new InfoPath with SharePoint 2013 book download.