Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: InfoPath

Problems publishing SPD 2010 workflows and InfoPath forms

So I have been working on a project on SharePoint 2010 that is basically a list where the input form has been customized using InfoPath.  We decided to go this route using a list instead of using a Forms library because of the capability in workflow to send dynamic emails to multiple people if and only if it comes from a Person or Group column.

The problem came when I got up around 75 columns.  Suddenly, I found that I could no longer publish the InfoPath form.  I got “The SOAP Message could not be parsed.”  If I removed a column or 2, or got rid of a rule, or a data connection, suddenly I could publish again.  But there seemed to be this magic, undefined breaking point that I couldn’t exceed before I’d start running into problems.

So now, I had to implement the SharePoint Designer 2010 workflow.  I knew I needed probably 3 workflows, one for create, and 2 for on change.  However, once I got past 2 or 3 IF statements, I could no longer publish the workflow either.  It would save, but on publish it would give me “Unexpected error on server associating the workflow.”  Now mind you, I did NOT have any Start Approval Process or similar actions – one of those content-type actions.  These were just simple conditions, emails, and setting of a few fields.

I found that I had to break my work up in to 16 workflows.  Each one started out with a condition to see if it should run.  This was getting ridiculous, and finally I got to a point where I couldn’t get any more granular and still it wouldn’t publish.  As suggested by some posts, I tried increasing $app.UserDefinedWorkflowMaximumComplexity in PowerShell (where $app is the SPWebApplication in question) and I also tried adding a timeout value to the  web.config.

 

So this whole time, I’m working from my contractor’s computer.  The SharePoint system in question was exposed over the internet using a UAG appliance for firewall and authentication.  Normally, this is not an issue, as I can usually get everything done I need to. On a whim I decided to open everything up from the customer’s computer, which incidentally has a VPN connection to their system, and voila, I can publish InfoPath forms and Workflows now with no issue.  <sigh>  It’s the little things that get you.

 

http://fairulshahrizat.blogspot.com/2012/12/sharepoint-designer-2010-workflow-error.html

 

Redesign your coded InfoPath 2010 Form to handle 1000’s of records

Scenario

Let’s say you have an InfoPath form with Code Behind where you want to have the user paste in thousands of Job Codes or Organization Codes or Cost Codes into a text box, and then they would press a button and it would validate those records – the Codes – against a System of Record (like a list in SharePoint) and then add the records into a Repeating Table in your InfoPath form.  You want to do this because your Codes have metadata associated with them that you either want to display and/or have the user update, and you want to have conditional formatting and buttons on each record to make it look and function the way you want it to.

You will quickly find that doing this in a Web Browser-enabled form is near to impossible, requires too many configuration settings to change in various places, and performs really sluggishly any time you take an action on the form like clicking a checkbox or pressing a button that only runs rules.  You’ll run into InfoPath settings in Central Admin that you have to configure, and you’ll run in to the dreaded 6-minute default timeout of IIS pages waiting for responses from the server.

The first step I took, on the advice of Doug my co-worker, Architect extraordinaire, was to offload all of the individual item processing – the validation against the SOR – off to a custom SOAP web service.  This did the heavy lifting – I passed in a comma-separated string of all of the Codes (which is why it had to be a SOAP service and not a REST service – too much data to pass in a URL), and then it validated the codes.  Finally, I had the web service create an XML Document with exactly the structure of my repeating table in InfoPath, fill it in with all of the Codes and their metadata, and pass that Outer XML as a string back to the form.  I used XPathNavigator to then insert the XML directly into the form.

What I found, through constructing a timing debug string, was that my Web Service call was taking 2 – 4 seconds.  Inserting the XML and waiting for the XML to render into HTML was taking 4 – 10 minutes, depending on how many codes I had.  It was completely unacceptable as I had clients who needed to put in 2000 codes.  My form got sluggish at 300 and outright unusable over 700.

Solution

So I went back to Doug, which is what I usually do when I’m stuck on a problem.  I explained where the bottleneck was and confirmed my suspicions by reading up on Performance and InfoPath on a series of 6 Microsoft blogs.  Doug said, since the bottleneck is the size of the XML in the form, you have to get the XML out of the form, into some other storage mechanism.  Allow the form to hold no more than 100 items at a time in the repeating table, and implement Paging of sorts to get at the other 1900 records.

Wow, sounded like a great idea so I tried it, and so far it seems to be working.  Because I’m getting paid to do this, I can’t give you all of the code.  However, here’s the general steps you will need to take in order to accomplish this idea:

  1. Create a SOAP Web Service.  I had done this before and put it in ISAPI, but you have to do some extra manual work to get it to work there.  I found that if you add it directly to a subfolder under the Layouts directory, you can call it from there without any extra work.  The Web Service will need to have 4 methods or so:
    1. AddSQLDataGetPageXML(string identifier, int pageNum, string codes, string extraMetadataIfNecessary, string xmlChanges, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier, or creating a new identifier if none exists;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items);
      4. (d) doing the actual Validation of the Code against your SOR;
      5. (e) adding the valid (and Invalid) codes to both the Generic List, as well as adding the record to the SQL table;
      6. (f) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      7. (g) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      8. (h) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    2. GetPageXML(string identifier, int pageNum, string xmlChanges, int oldPageNum, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items) – NOTE HERE – this has to handle deletions too, so you need some special manipulation of comparing what is (or rather is NOT) in the XML string of changes, versus what IS in your Generic List that you read out of SQL;
      4. (d) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      5. (e) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      6. (f) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    3. ProcessChanges(string identifier, string xmlChanges, string ns, int startItem, int stopItem, SqlConnection conn, List<YourCustomCodeObject> allCodesFromSQL)
      This is the method that you call from the first 2 methods to process your existing changes.  Remember, if the user changes something on the current page, and then you switch to another page, you need to make sure that you capture what has been changed on the current page.
    4. ReValidateCode(string code, string otherMetadataHere)
      This method is responsible for

      1. (a) reading in the code and finding it / validating in against your SOR;
      2. (b) creating a custom Return Class that can hold your Code’s data and metadata, and whether or not it’s valid;
      3. (c) returning the custom Return Class (your InfoPath form handles the changing of the repeating table item in the repeating table)
  2. Hook this up to your InfoPath Form
    1. There’s too much involved to go into here, but you need to basically do the following:
      1. Have a SOAP web service Data Connection, and methods to set values into and Execute that connection.
      2. Have a text box to paste the Codes into, and a method to take those codes, wrap them up and send it to the SOAP web service
      3. Have a repeating grid with all of your fields, metadata fields, buttons (such as ReValidate), and conditional formatting
      4. Have buttons for Previous and Next that have conditional formatting on them to only show if it is valid to show them (i.e. don’t show Previous if you’re on page 1)
      5. Have a MoveToPage method that gets called from the buttons and optionally some other places (like a delete all on this page kind of method)
  3. Create your Workflow (a custom Console Application probably) that handles the business processes of the form
    It will do the following:

    1. Loads in the InfoPath form, which you have created a class for using the XSD of the InfoPath Form (Export to Source Files to find the XSD)
    2. Checks to see if it’s already processed
    3. Looks for the Identifier in the Form that got stored when you were using the form
    4. Looks in SQL using the Identifier to get all the records
    5. Processes the records
    6. Deletes the records out of SQL

 

InfoPath 2010 – Registering of form templates deployed through object model is not supported

In an earlier post, https://suehernandez.wordpress.com/2013/11/22/deploy-infopath-2010-with-code-as-solution-feature/, I explained how to implement an InfoPath 2010 form as a Feature in SharePoint 2010.

As I was trying to deploy this feature, I kept getting the error:

Registering of form templates deployed through object model is not supported

 

It turns out that what happened is that up until that point I had been deploying my InfoPath forms through Central Administration.  It’s easiest and usually quickest to do it this way (although once you deploy the same form like 50 times it starts getting really slow to deploy).

But I had not changed the “Identifier” per se of the form.  Let me show you a couple of pictures that shows what I’m talking about:

Form Template Properties buttonForm Template Properties dialog

Where it says “urn:schemas-microsofr-com….” that is the identifier of the form.  If you publish the form, and then copy it and change the name of it, it does NOT change the identifier.  So if you’ve already got that ID in Central Admin under Manage Form Templates, and then you try to deploy the same form as a feature, you get this error message.

One of the ways you can fix this is to either manually change this URN ID, or just open up the form in InfoPath Designer in design mode and do a Save-As.  It will automatically change your ID to match the new name of the form.

In the mean time, delete the form from Central Admin and try your Solution Deployment again.

Deploy InfoPath 2010 with Code as Solution-Feature

This one will be somewhat long of a post.   Here we will examine deploying an InfoPath 2010 form with CodeBehind as a custom Feature to SharePoint 2010.

  1. Change the properties of your form to Full Trust

    Full Trust

    Full Trust

  2. Publish your form to a SharePoint Server

    Publish Step 1

    Publish Step 1

  3. Choose your URL to the site where you’re going to be using the file.  Note that it will put this form in the Form Templates (url is FormServerTemplates) library at the top of your Site Collection

    Publish Step 2

    Publish Step 2

  4. Make sure that it is checked off to enable the form to be filled out by a browser, and choose Administrator Approved Template

    Publish Step 3

    Publish Step 3

  5. Choose the location on your desktop or local computer or shared drive where you want to save the Published form template to

    Publish Step 4

    Publish Step 4

  6. Create your Visual Studio solution.  Create it as a SharePoint 2010 Empty SharePoint Project

    Create new Empty SP Project

    Create new Empty SP Project

  7. Choose to deploy as Farm Solution

    Deploy as Farm Solution

    Deploy as Farm Solution

  8. Add a Feature to the project

    Add Feature

    Add Feature

  9. Rename the feature to something that makes more sense, so you can more easily find it later through PowerShell or in the 14 hive

    Rename Feature

    Rename Feature

  10. Open the feature in Visual Studio into Design view and set the properties of the feature at the top.

    Set Properties of Feature

    Set Properties of Feature

  11. Add a Module to the project

    Add Module

    Add Module

  12. Rename the module and remove the Sample.txt file

    Rename Module and remove Sample txt

    Rename Module and remove Sample txt

  13. Add your Module Files.  Here you will be adding the published form that you completed in Step 5 here, as well as the dll.  (You can get the .dll by going into design view in InfoPath, pressing Publish, and pressing Export Source Files – give it a folder to put the source files in, and find your .dll in there)

    Note that I have a little naming change here versus what I have in step 5.  I am showing you a “real” project file and had to blot out part of the name.  But this will be your published file – not the file you use to make the changes to the form.

    Add Module Files

    Add Module Files

  14. Open the Elements file in the NewFormModule.  You will have to make changes.  Here’s the file as it looks before:
    Elements Before

    Elements Before

    and here’s the file as it looks after:

    Elements After

    Elements After

  15. Open back up the Feature in Design mode, and press the button to switch it to Manifest mode.  Expand Edit Options and add the 2 properties shown below, with your Feature Name and your Published InfoPath form name as the values:

    Update the Manifest

    Update the Manifest

  16. Get the Properties window up for the Feature.  You will be adding a Receiver Assembly and a Receiver Class
    ReceiverAssembly=”Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”
    ReceiverClass=”Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver”

    Feature Props add Receiver info

    Feature Props add Receiver info

  17. OPTIONAL:  Add Activation dependency.  NOTE:  if you are going to mark your feature as Hidden=TRUE, you cannot add this dependency.  We will discuss later why you may want to make your feature Hidden.
    <ActivationDependency FeatureId=”C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5″/>

    Feature Activation Dependencies

    Feature Activation Dependencies

  18. Now in your solution explorer, you will want to highlight the Elements file and get the properties.  Clear out the Deployment Path and make sure it’s of type ElementManifest

    Elements Properties

    Elements Properties

  19. Next highlight the XSN published form and get its properties.  Clear the Deployment Path and make sure it is of type ElementFile

    Form Properties

    Form Properties

  20. Repeat the previous step for the DLL file
  21. Open up your Feature in Design mode just to look at it.  Your Manifest file should look similar to the following (if you added the Activation Dependency):

    Manifest After

    Manifest After

Making the feature Hidden

The problem with adding the published file to Central Administration – Managed Form Templates is twofold:  (1) It puts in every version you create into the manifest; and (2) It makes the feature available to EVERY site collection in your Farm.  Similarly, if you don’t make the feature Hidden, all site collections will be able to see this feature.

Depending on your project, you may opt to hide the feature so that no one can turn it on, and only activate it to the Site Collection using PowerShell.

If you choose to make your feature hidden, you cannot have the Activation Dependency of the Enterprise Features.  Just realize this means is that if you don’t have Enterprise Features turned on, this Content Type will not work.

Here’s a picture of where you’d make the feature hidden – it’s just another one of the properties on the feature:

Is Hidden is True

Is Hidden is True

Deploy the feature, Activate it, and Use it

Once you deploy the solution package (.WSP file) and activate it to the site collection you’re going to use it in, it will now be a Content Type that you add to your Form Library.  Any fields which you promoted in the Publishing process will automatically be added to your Form Library and will sync to the data in the form.

References

I got bits and pieces from a lot of places, but here’s some main ones that I got a lot of detail from:

SharePoint 2013, InfoPath and Claims – GetUserProfileByName

You would not believe the hoops you have to go through to get data auto-populated in an InfoPath Form if you’re using Claims-based authentication, which I believe is the default in SharePoint 2013.

The Scenario:

You have an on-premise SharePoint 2013 Server installation with InfoPath Forms Services (so you have enterprise) and you want to create an InfoPath form that auto-populates a logged in user’s ID, email, and phone number.  You are using Claims-based authentication in the web application in question.

In this scenario, I’m using InfoPath Designer 2010 – I haven’t upgraded to 2013 yet, but I don’t think it makes a difference.

The Problem:

When you use Claims-based authentication, your user name is prefaced by “0#.w|”.  So for example, if your user name is SuesDomain\jdoe then your Claims-based user token will be, without the quotes:     “0#.w|SuesDomain\jdoe”

InfoPath can’t handle that, or more specifically, the UserProfile.asmx method GetUserProfileByName method can’t handle that.  InfoPath tries to pass in your Claims-based user token instead of your domain\User Name.

You get 2 problems – you have an authentication problem where the currently logged in user is not allowed to hit the web service, so you get an Access Denied 401 authentication error.  The second problem is getting the right user from the web service.

The Solution:

The steps you have to take are listed here.  See the reference posts for more details and pictures.

  • Have your Active Directory team create you a User Name and Password for a “generic” InfoPath Account.  The one I created was called Domain\SP2013_IPRdr for “InfoPath Reader”.
  • Have the Central Administration team create a Secure Store service application, if they don’t already have one.
    • Generate the Secure Store key if you haven’t already.
    • Create a new application in the Secure Store called something like InfoPathGUPBN (“GetUserProfileByName” is what the acronym stands for); target application type is Group, and User Field Types are the Windows User Name and Password.
    • Set the credentials:  Whomever you want to administer the application should be set as the Target Application Administrators and for the Members, select All Users.
    • Set the Credentials to the Application and use the User Name and Password that you received from the A/D team, example Domain\SP2013_IPRdr.
  • In SharePoint 2013 – in the site you wish to publish your form to, create a Data Connection library.
  • Go Into InfoPath and go to your GetUserProfileByName secondary data connection.  If you don’t already have one, you can create one the “normal” way, making sure you do not choose a user name (just like you would normally do).  For an example of how to set one up, see the first reference in the section at the bottom of this post.
  • Modify the GetUserProfileByName secondary data connection so that it does NOT automatically load the data upon form load.  This is not required, but saves performance.
  • Convert the GetUserProfileByName secondary data connection into a UDCX file (“Convert to Connection File”) – store it in the Data Connection Library that you created a few steps ago.
  • Download the UDCX file that you just created from your SharePoint 2013 Data Connection library and put it on your desktop.  Edit it in Notepad.
  • Change the commented out line to read something like this (depending on what you called your Secure Store Application):
    <udc:Authentication><udc:SSO AppId='InfoPathGUPBN' CredentialType='NTLM' /></udc:Authentication>
  • Upload the UDCX file back in to the Data Connection Library.
  • Publish your newly changed InfoPath form into your SharePoint 2013 InfoPath Form Library
  • Make sure to give your special A/D account, for example the Domain\SP2013_IPRdr account, full read access to that site, so that they have permissions to hit the web service.

All of the above was just to get you to Authenticate properly with the Web Service.  If you were to stop now, and try to get the information from the web service you would get the information for the user Domain\SP2013_IPRdr.  Not the logged in user.  Now you have to take the following steps to get the real logged in user:

  • Go into InfoPath and go to create a Form Load rule
  • For the first rule, call it “Set User Profile” or something similar
    • Create an action to Set a Field’s Value
    • Choose the field – it’s the GetUserProfileByName secondary data connection, in the queryFields node, all the way in:  AccountName.
    • Set it to the following formula value:
      substring-after(userName(), "0#.w|")
    • Add another action, Query for Data, and choose your GetUserProfileByName secondary data connection
  • For the second rule, call it something like “Set Fields”
    • This is where you fill in your fields from the data source.  To find a good example of how to do this, see the first reference in the section at the bottom of this post.

Bottom line:  Pain in the rear.

References:

Publish Error in InfoPath 2010 after pressing Cancel

So I went to publish my InfoPath 2010 Form into SharePoint 2010.  I was impatient because it was taking a long time and I needed to change just one more thing.  So I pressed Cancel.

Now I’m getting one of 2 error messages when trying to either Quick Publish or Regular Publish:

“InfoPath cannot save the following form: https://servername/web/subweb/infoPathLib The following file(s) have been blocked by the administrator …”

“InfoPath cannot save the following form:  https://servername/web/subweb/infoPathLib This document library was either renamed or deleted, or network problems are preventing the file from being saved.  If this problem persists, contact your network administrator.”

Well, after trying numerous times to publish and I even changed the InfoPath Form Properties, still no luck.  Sounds pretty dumb, but all I did was to completely close out all instances of InfoPath and my browser (maybe not necessary) and then re-open InfoPath and do a Manual Publish (not a Quick Publish).  I didn’t change any url’s or put in IP addresses or anything like that.

Are you having this trouble?  Does it persist after you have completely closed and re-opened?  Leave me a coment – maybe together we’ll put in a bug report to Microsoft.

~ Sue

Basic InfoPath Tips and Tricks

I just had a great seminar yesterday on InfoPath and I thought I’d share some of the tips and tricks that we discussed, as well as a few that we did not.

In general, these are the steps you should take, in this order, to prepare a form for use in SharePoint:

  1. Create the InfoPath Forms Library on your site.
    You will need this URL when creating connections such as a Submit connection.
  2. Create the new .xsn file (InfoPath Form).
    I recommend using a blank template when working with InfoPath 2007 – gives you much more flexibility.
  3. Create the Data Source for your form.
    Do this BEFORE you start to design your form – it will make your designing go more smoothly.  Create a wireframe mockup, for instance an indented bulleted list.  Use Groups liberally to act as containers for related sets of information.
  4. Lay out the general Design of the form.
    See below for a tip on my idea of form design.
  5. Add Buttons and other periferal controls to the form.
    For example a button to Save and Close the form
  6. Apply Formatting, Conditional Formatting, Totalling, Rules, Formulas.
    Use formulas and Default Values to come up with calculated fields. 
  7. Create Alternate Views.
    After you create one full form view, you can either copy that view and make modifications, or, in a Wizard-like form setup, you can continue on with the rest of your questions (see below on the tip for the Form Wizard).
  8. Create a Print View.
    You can create a view that is specifically for a report.  Make it a Read Only view, and place whatever other text or disclaimers
  9. Publish your Form to SharePoint
    Use the Publish feature in SharePoint 2007, or the Quick Publish feature in 2010 (you can’t use Quick Publish till you’ve published regularly once first).  Promote any columns you wish to use, and see below for a tip on field promotion.
  10. Configure the Forms Library to display as a web page.
    Go into Form Library Settings –> Advanced Settings and click the radio button “Display as Web Page”.
  11. Test your form using both priviledged and non priviledged accounts.
    Especially if your form contains logic or conditional formatting to only display certain controls to certain people.

Here are some basic Tips and Tricks I’ve come up with:

  • You do NOT need 2 SharePoint Libraries for your InfoPath forms – you only need one library which both holds the template (in the background) and holds the completed forms in the list.  Make sure you Publish to a Form Library and don’t just upload it.
    • That being said, you can always have a second library called “Form Templates” as a place to centrally store your design templates, but this is not where your users go to fill them out.
  • If you lose your Design Tasks pane, find it in the View menu in InfoPath 2007.  In InfoPath 2010, use the ribbon’s Data tab and select Show Fields to see your data source.
  • In InfoPath 2007, when you want to have a rule that runs when the form is opened, you go to Tools –> Form Options –> Open and Save –> Rules (button) [for instance to set a user name field when they first open the form].  In InfoPath 2010, you go to the ribbon’s Data tab and press the Form Load button – this brings up the Manage Rules pane, but in the context of the form opening.
  • Groups, Groups, and more Groups – when creating your Data Source I recommend using Groups (they look like folders in the Data Source Pane) to categorize your fields into logical sections.  You can use groups within groups as well, if it makes logical sense to do so.
  • Use a Submit Button that you create, with a calculated file name, to both submit and close your form.  That way the users will not have to come up with a file name or get confused on which toolbar buttons to press.  You can create a data field called fileName and populate it on form load – only under the condition that it hasn’t already been filled in – to the “concat” of the user name of the originator plus the “now” date time function.
    • If you choose to go this route, go to Tools –> Form Options –> Browser (InfoPath 2007) or File –> Advanced form options –> Web Browser (InfoPath 2010) and remove the options for Save, Save As, and even Views (also control those with buttons)
  • When wireframing your data source, don’t forget to properly represent any table-like sections – implemented as “repeating sections” in InfoPath.  For example, if you have expenses for one day, the expenses should be a “repeating group” to capture all of the columns for each row – each expense and its metadata.
  • Always rename the top node of the Data Source.  Don’t leave the default of myFields.  Rename it to what makes sense to that particular project.
  • Use the correct casing in names of fields (I think it’s called Title Case) to make the design of the form easier.  For example, if you create a field named EmployeeName (notice the E and the N are both capital), then when you drag the field on to the design surface, it knows to put “Employee Name” there for you (it inserts the space).
  • You can’t re-use field names anywhere in the Data Source – even if they’re in different “Nodes” or groups.  So if you need to, use a prefix, like TravelExpenditures and OtherExpenditures.
  • When designing your form – use a big ol’ layout table in the design surface.  Choose between 15 and 20 columns wide and about 30 rows long.  This is to line things up properly – you won’t have to play around with widths and spacing and you shouldn’t have to adjust the sizes of the columns.
    • When creating places for your data controls to go, merge together some cells, depending on where you want to place the controls, and then drag your controls into the merged area.  Either keep the default of the text on top, or merge cells to the left to hold the title.
    • Using tables in this manner is very helpful – you will note that when you use a table and drag in a control, the control automatically “fills” the width of the section you drag it in to, making things line up just nicely.
  • If you have a lot of conditional logic/branching (i.e. the questions they need to answer depend on the choices of questions above them), then you can set up a Wizard-like form using views and buttons.  Each View should be a “page” of questions that go together.  Then you create a button at the bottom of the view, right after one of the questions that needs conditional logic.  The button will have one or more rules – for example [If the IsOver500 equals True Then switch to the “Over 500 View”] and [If the IsOver500 equals False Then switch to the “500 and Under View”].
    • Don’t forget to add appropriate “Previous” buttons to make sure that the users can get back to the questions they have already answered.
  • If you have a need to make a lot of fields read-only, you have the option of creating a view with only those fields and making the entire View read-only instead of setting the Read Only property of every control.  This works well for Report or Print views.
  • When you promote fields during publish, be very aware of the choices in the dialog box.  You need to choose whether or not the columns should be Created in the library, whether you use Existing columns in the library, or, in cases of content types, if you want to use existing or new Site Columns.

Some References for you:

InfoPath: Importing a Spreadsheet

Have you ever wondered if you could import a spreadsheet into InfoPath?   

Well I have a scenario where we have users that are requesting for a purchase to happen – it’s not a purchase order yet.  They need to put in several line items into a recurring table by importing a spreadsheet / CSV file.

What I did, was I put an attachment on the form called ImportSpreadsheet.  During my custom Visual Studio Workflow, IF the file exists, I process the spreadsheet.  I haven’t completed the code yet to process the spreadsheet itself(SEE UPDATE), but I did figure out how to add items to a recurring section.  Below, in my code sample, _purchaseRequest is the serialized InfoPath Document.

Let me know if you have any questions or need more information. 

UPDATE:  I have updated the code with all the refs I used, as well as the updated code for parsing the CSV file.

REFS:

        private void ImportSpreadsheet()
        {
            if (_purchaseRequest.ImportSpreadsheet != null && _purchaseRequest.ImportSpreadsheet.Length > 0)
            {
                // Add items to the Items List
                try
                {
                    SPEventManagerWrapper.DisableEventFiring();

                    SPFile docFile = workflowProperties.Item.File;

                    // http://weblogs.asp.net/jan/archive/2004/05.aspx?PageIndex=2
                    while (docFile.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                    {
                        System.Threading.Thread.Sleep(1000);
                        docFile = workflowProperties.Web.GetFile(docFile.UniqueId);
                    }

                    using (Stream formStream = docFile.OpenBinaryStream())
                    {
                        // Load the Existing Document
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.PreserveWhitespace = true;
                        xmlDoc.Load(formStream);

                        XmlNamespaceManager xmlns = new XmlNamespaceManager(xmlDoc.NameTable);
                        xmlns.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-12-22T15:57:36");

                        XmlNode itemizedListNode = xmlDoc.SelectSingleNode("/my:PurchaseRequest/my:ItemizedList", xmlns);
                        XmlNode subTotalNode = xmlDoc.SelectSingleNode("/my:PurchaseRequest/my:ItemizedList/my:Subtotal", xmlns);

                        double subTotal = 0;
                        double taxTotal = 0;
                        double totalWithTaxTotal = 0;

                        int lineItemNumCounter = 1;

                        string csv = System.Text.ASCIIEncoding.ASCII.GetString(_purchaseRequest.ImportSpreadsheet);
                        DataTable dt = CsvParser.Parse(csv, true);
                        int rowCounter = 0;
                        foreach (DataRow r in dt.Rows)
                        {
                            // Account for Header Row
                            if (rowCounter == 0)
                            {
                                rowCounter++;
                                continue;
                            }

                            string lineItemNum = lineItemNumCounter.ToString();
                            string lineItemDesc = r["Item Description"].ToString();
                            string partNumber = r["Vendor Part #"].ToString();
                            int quantity = 1;
                            if (!r.IsNull("Quantity"))
                            {
                                string qtyString = r["Quantity"].ToString();
                                qtyString = qtyString.Replace(" ", "");
                                if (!string.IsNullOrEmpty(qtyString))
                                {
                                    quantity = Convert.ToInt32(qtyString);
                                }
                            }
                            double unitPrice = Convert.ToDouble(r["Unit Price"]);
                            double taxPercent = 0;
                            double preTaxPrice = unitPrice * quantity;

                            double hiddenTax = 0;  // Convert.ToDouble(string.Format("{0:0.00}", (preTaxPrice * .01 * taxPercent)));
                            double totalWithTax = preTaxPrice + hiddenTax;

                            XmlNode itemNode = xmlDoc.CreateElement("Item", xmlns.LookupNamespace("my"));
                            itemNode.AppendChild(xmlDoc.CreateElement("LineItemNumber", xmlns.LookupNamespace("my"))).InnerText = lineItemNum;
                            itemNode.AppendChild(xmlDoc.CreateElement("ItemDescription", xmlns.LookupNamespace("my"))).InnerText = lineItemDesc;
                            itemNode.AppendChild(xmlDoc.CreateElement("PartNumber", xmlns.LookupNamespace("my"))).InnerText = partNumber;
                            itemNode.AppendChild(xmlDoc.CreateElement("Quantity", xmlns.LookupNamespace("my"))).InnerText = quantity.ToString();
                            itemNode.AppendChild(xmlDoc.CreateElement("UnitPrice", xmlns.LookupNamespace("my"))).InnerText = unitPrice.ToString();
                            itemNode.AppendChild(xmlDoc.CreateElement("TaxPercent", xmlns.LookupNamespace("my"))).InnerText = taxPercent.ToString();
                            itemNode.AppendChild(xmlDoc.CreateElement("PreTaxPrice", xmlns.LookupNamespace("my"))).InnerText = preTaxPrice.ToString();
                            itemNode.AppendChild(xmlDoc.CreateElement("TotalWithTax", xmlns.LookupNamespace("my"))).InnerText = totalWithTax.ToString();
                            itemNode.AppendChild(xmlDoc.CreateElement("HiddenTax", xmlns.LookupNamespace("my"))).InnerText = hiddenTax.ToString();

                            itemizedListNode.InsertBefore(itemNode, subTotalNode);

                            subTotal += preTaxPrice;
                            taxTotal += hiddenTax;

                            lineItemNumCounter++;
                        }

                        totalWithTaxTotal = subTotal + taxTotal;

                        xmlDoc.GetElementsByTagName("my:Subtotal")[0].InnerText = subTotal.ToString();
                        xmlDoc.GetElementsByTagName("my:TaxTotal")[0].InnerText = taxTotal.ToString();
                        xmlDoc.GetElementsByTagName("my:OrderTotal")[0].InnerText = totalWithTaxTotal.ToString();

                        formStream.SetLength(0);
                        formStream.Seek(0, SeekOrigin.Begin);
                        xmlDoc.Save(formStream);

                        docFile.SaveBinary(formStream);
                    }
                }
                catch (Exception ex)
                {
                    continueWorkflow = false;
                    ErrorMessages = ex.ToString();
                    WorkflowOutcome = "Error";
                }
                finally
                {
                    SPEventManagerWrapper.EnableEventFiring();
                }

                resetInfoPathForm();
            }
        }
    //http://knab.ws/blog/index.php?/archives/10-CSV-file-parser-and-writer-in-C-Part-2.html
    public class CsvParser
    {
        public static DataTable Parse(string data, bool headers)
        {
            return Parse(new StringReader(data), headers);
        }

        public static DataTable Parse(string data)
        {
            return Parse(new StringReader(data));
        }

        public static DataTable Parse(TextReader stream)
        {
            return Parse(stream, false);
        }

        public static DataTable Parse(TextReader stream, bool headers)
        {
            DataTable table = new DataTable();
            CsvStream csv = new CsvStream(stream);
            string[] row = csv.GetNextRow();
            if (row == null)
                return null;
            if (headers)
            {
                foreach (string header in row)
                {
                    if (header != null && header.Length > 0 && !table.Columns.Contains(header))
                        table.Columns.Add(header, typeof(string));
                    else
                        table.Columns.Add(GetNextColumnHeader(table), typeof(string));
                }
                row = csv.GetNextRow();
            }
            while (row != null)
            {
                while (row.Length > table.Columns.Count)
                    table.Columns.Add(GetNextColumnHeader(table), typeof(string));
                table.Rows.Add(row);
                row = csv.GetNextRow();
            }
            return table;
        }

        private static string GetNextColumnHeader(DataTable table)
        {
            int c = 1;
            while (true)
            {
                string h = "Column" + c++;
                if (!table.Columns.Contains(h))
                    return h;
            }
        }
        private class CsvStream
        {
            private TextReader stream;

            public CsvStream(TextReader s)
            {
                stream = s;
            }

            public string[] GetNextRow()
            {
                ArrayList row = new ArrayList();
                while (true)
                {
                    string item = GetNextItem();
                    if (item == null)
                        return row.Count == 0 ? null : (string[])row.ToArray(typeof(string));
                    row.Add(item);
                }
            }

            private bool EOS = false;
            private bool EOL = false;

            private string GetNextItem()
            {
                if (EOL)
                {
                    // previous item was last in line, start new line
                    EOL = false;
                    return null;
                }

                bool quoted = false;
                bool predata = true;
                bool postdata = false;
                StringBuilder item = new StringBuilder();

                while (true)
                {
                    char c = GetNextChar(true);
                    if (EOS)
                        return item.Length > 0 ? item.ToString() : null;

                    if ((postdata || !quoted) && c == ',')
                        // end of item, return
                        return item.ToString();

                    if ((predata || postdata || !quoted) && (c == '\x0A' || c == '\x0D'))
                    {
                        // we are at the end of the line, eat newline characters and exit
                        EOL = true;
                        if (c == '\x0D' && GetNextChar(false) == '\x0A')
                            // new line sequence is 0D0A
                            GetNextChar(true);
                        return item.ToString();
                    }

                    if (predata && c == ' ')
                        // whitespace preceeding data, discard
                        continue;

                    if (predata && c == '"')
                    {
                        // quoted data is starting
                        quoted = true;
                        predata = false;
                        continue;
                    }

                    if (predata)
                    {
                        // data is starting without quotes
                        predata = false;
                        item.Append(c);
                        continue;
                    }

                    if (c == '"' && quoted)
                    {
                        if (GetNextChar(false) == '"')
                            // double quotes within quoted string means add a quote
                            item.Append(GetNextChar(true));
                        else
                            // end-quote reached
                            postdata = true;
                        continue;
                    }

                    // all cases covered, character must be data
                    item.Append(c);
                }
            }

            private char[] buffer = new char[4096];
            private int pos = 0;
            private int length = 0;

            private char GetNextChar(bool eat)
            {
                if (pos >= length)
                {
                    length = stream.ReadBlock(buffer, 0, buffer.Length);
                    if (length == 0)
                    {
                        EOS = true;
                        return '';
                    }
                    pos = 0;
                }
                if (eat)
                    return buffer[pos++];
                else
                    return buffer[pos];
            }
        }
    }
 

MOSS VPC – Publishing from Infopath 2007 URL Not Valid

For some reason when you’re in a Virtual PC that is not on the network, and you try to publish an InfoPath Form to your server, you receive a “URL Not Valid” error.

This is due to the System Event Notification Service checking the network status.  A trick is to type in

    net stop sens

in a command window, and then deploy your template.

 

Reference:  http://jopx.blogspot.com/2006/08/having-problems-with-moss-2007-in.html