Thursday 21 January 2010

Side-By-Side Versioning of InfoPath Browser Based Forms using SharePoint 2007 (aka how to have Multiple versions the same Form in the Same Forms Library)

When you have an InfoPath form live on your server, and you make a change to the schema of the form and upload it over the existing copy, your old forms won't open. You will just get a "Form has been closed" message or another kind of supporting error. To avoid killing off old versions of forms when deploying new versions, you simply have to do the following:
  1. Publish the form with a new name e.g. PurchaseOrderForm_V1-01.xsn using the Publish wizard either in InfoPath of Visual Studio 2008. Note that you cannot just publish and rename it as this will not change the FormId and instead potentially upgrade all your old forms to an incompatible version.




  2. Upload the file to the server via Central Admin > Applications Management > Upload Form Template
  3. Activate the form to your site collection. Look at the properties of the form from the Central Administration > Application Management > Manage Form Templates > View Properties - it should have the version in the form Id (e.g. urn:schemas-microsoft-com:office:infopath:PurchaseOrderForm-V1-01:-myXSD-2009-11-02T03-05-56)
  4. Go to My Form Library (e.g. "Purchase Order Forms" > Settings > Form Library Settings. Where the current content type is used.
  5. Add the new content type PurchaseOrderForm_V1-00 to the list.


  6. Rename the old Content Type (ie the current one in use that you want to preserve) to PurchaseOrderForm_V1-00 (this is typically the Name of the "Parent content Type"
  7. Rename the newly uploaded content type from PurchaseOrderForm_V1-00 to a friendly name the same as the old version e.g. "Purchase Order Form"



  8. Go to "Purchase Order Forms" > Settings > Form Library Settings > Change New Button Order and Default Content Type
  9. Hide the old content type as you don't want new instances created (e.g. hide "PurchaseOrderForm_V1-00") and show the new version "Purchase Order Form". It should be selected as the default content type.
  10. Finished - old versions of your form will still open without error and appear as they used to, and the new versions will open with the updated code base and schema.

    Also See http://www.delphi-ts.com/blogs/lozzi/post/2009/07/08/Versioning-Your-Published-InfoPath-Forms.aspx which has similar info. I also confirmed that this works if your Forms reference common code Dlls - the assemblies required for the forms are also not blown away as you are not upgrading.

Wednesday 20 January 2010

Problem and Fix: Microsoft.SharePoint.WebControls.SPDatePickerControl doesn't render Dates based on Regional Settings

I had a problem today that a SharePoint Application Page (ie an ASP.NET page hosted in the SharePoint _layouts directory under the 12 hive) I use wasn’t rendering the SharePoint Date Time Control date format according to SharePoint regional settings.  No matter what setting I tried, it would not change the format that was rendered - instead it was using the default US date format. This is disconcerting for users because all the other pages in the site render the dates correctly. Even the InfoPath forms hosted via Forms Services (which make use of the Application pages as lookup forms) render the date format as per Australian regional settings.

 Some of the things I tried to resolve/check the issue:

  1. Regional Settings under Site Settings -> Regional Settings did not make any difference.

  2. Checked Internet Explorer 7.0 browser settings in Tools->Options->Language – which is already English [AU]

  3. I tried to override the InitializeCulture() handler and set the Thread.Culture (ie date format etc) and UICulture (which resource files that ASP.NET uses) – but it made no difference.

    /// 
            /// Force application page to render in Australian format as SP Date Control
            /// support standard SharePoint regional Settings
            /// 
            protected override void InitializeCulture()
            {
                Thread.CurrentThread.CurrentCulture =
                    CultureInfo.CreateSpecificCulture("en-AU");
    
                Thread.CurrentThread.CurrentUICulture = new
                            CultureInfo("en-AU");
                base.InitializeCulture();
            }
    


However none of the solutions above did a thing. By luck I noticed through debugging that there is a property called “LocaleId” on the Microsoft.SharePoint.WebControls.SPDatePickerControl ( as per http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.spdatepickercontrol.aspx). When I set this to the LocaleId=3081 (for English(Australian)), then the SharePoint Date control renders the date format correctly (ie in dd/MM/yyyy format for Australian users rather than the default US date format of MM/dd/yyyy).

This LocaleId could also be set programatically based on a config file if you don't want to hardcode the locale into your SharePoint Application Page.


Monday 18 January 2010

How to Create Subfolders within Subfolders in SharePoint 2007 via the Lists.asmx Web Service

I did an entry on the deletion of folders via the Lists.asmx web service back in July 2008 which can be found here (http://ddkonline.blogspot.com/2008/07/deleting-folders-in-moss-via-web.html) - but I failed to include how to create folders using the same UpdateListItems() mechanism.

A sample of creating a folder and subfolders within SharePoint 2007 lists can be found below:

/// <summary>
        /// As checked in U2U CAML query builder, this is valid in 
        /// <Batch PreCalc='TRUE' OnError='Continue' RootFolder='/Purchase Order Forms V2'>
        ///<Method ID='1' Cmd='New'><Field Name='ID'>New</Field><Field Name='FSObjType'>1</Field><Field Name='BaseName'>DDkTest2</Field></Method></Batch>
        /// THIS Works for Subfolders
        /// <Batch PreCalc='TRUE' OnError='Continue' RootFolder='/Purchase Order Forms V2/2010_01_January'>
        /// <Method ID='1' Cmd='New'><Field Name='ID'>New</Field><Field Name='FSObjType'>1</Field><Field Name='BaseName'>DDkTest2</Field></Method></Batch>
        /// </summary>
        private void CreateFolder(string listName, string rootSubFolderName, string newFolderName)
        {
            ListsSoapClient client = new ListsSoapClient(); //TODO: Change to use WCF Client Factory

            //Correct invalid characters
            newFolderName = newFolderName.Replace(":", "_");
            string rootFolder = rootSubFolderName.Length > 0 ? string.Format("/{0}/{1}", listName, rootSubFolderName) : listName;
            string xmlCommand = string.Format("<Method ID='1' Cmd='New'><Field Name='ID'>New</Field><Field Name='FSObjType'>1</Field><Field Name='BaseName'>{1}</Field></Method>", rootFolder, newFolderName);
            client.ClientCredentials.UserName.UserName = ConfigHelper.SharePointServiceUserName;
            client.ClientCredentials.UserName.Password = ConfigHelper.SharePointServicePassword;
            XmlDocument doc = new XmlDocument();
            System.Xml.XmlElement batchNode = doc.CreateElement("Batch");
            batchNode.SetAttribute("OnError", "Continue");
            //Insert / to front as it is required by web service.
            if  (!rootFolder.StartsWith("/"))
                rootFolder = string.Format("/{0}",rootFolder);

            batchNode.SetAttribute("RootFolder", rootFolder);
            batchNode.InnerXml = xmlCommand;
            XmlNode resultNode = client.UpdateListItems(listName, batchNode;
        }

Friday 15 January 2010

Zach Klein's First Steps - while holding a remote and kicking a soccer ball!

It's a very proud moment for me - my wee laddie Zach has finally started walking by himself at 14 months of age. Not only that - he did it while holding a remote control and then kicks a NERF-style soccer ball to finish it off. Find the first "tubed" vid of my son speeding around the living room at:

http://www.youtube.com/watch?v=_86Zx0CKnvA

Dynamically Modifying the Submit Location of InfoPath Forms using SharePoint Forms Services using Code Behind

It is a well known fact (e.g. http://blogs.msdn.com/sharepoint/archive/2007/07/25/scaling-large-lists.aspx) that SharePoint performance can decline significantly when there are more than 2000 items in a SharePoint "container"  (e.g. the root folder of a Document Library). To avoid this, you can store your documents in separate lists or you can store them in separate subfolders within your list.

On issue is that InfoPath 2007 doesn't support dynamic saving to subfolders out of the box (it only supports dynamic file names). You need to perform a code-based workaround to get it happening. In the code sample below, I dynamically submit to a folder location (ie a subfolder within a Document Library or List) based on the current date. All form submissions in this way are grouped by Year and Month (the client is not likely to have more than 2000 forms submitted in one month).

public void submitButton_Clicked(object sender, ClickedEventArgs e)
        {
            ////Need to submit the form to a programatically determined subfolder location (based on the date)
            //Folder format is 2010_01_January (YYYY_MM_MMMMM)
            FileSubmitConnection submitConnection = (FileSubmitConnection)DataConnections["SharePoint Library Submit"];
            submitConnection.FolderUrl = string.Format("{0}/{1}_{2}_{3}",submitConnection.FolderUrl ,
                DateTime.Today.Year, DateTime.Today.Month.ToString("00"), 
                System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.GetMonthName(DateTime.Today.Month)); 
            submitConnection.Execute();
            ////TODO: Need to create folder with SP Object Model to hold attachments as well before saving them there.
        }