Sunday, September 20, 2009

Attach Files to InfoPath Form, (upload to Sharepoint library and generate links into form’s body)

Soon I have a task to create InfoPath form which has Attachment control, uploads file(s) to Sharepoint library and puts a link to it in the form’s body. I will describe the steps, which a used to solve the business scenario.

I choose to use Visual Studio 2008 instead of InfoPath client and created new InfoPath Form Template project.

image

I designed a form with following layout and data source:

image

image

Field Type Description
Title Text Required, Text Box
Attachment Picture or file attachment (base64)  
group4 Repeating table Two columns, Container for hyperlinks
Url Text Text Box with conditional formatting (see the settings below)

Conditional formatting for Url text box:

image

image

image

Select a hyperlink control from Toolbox, drag and drop it in the first column of the repeating table

image

and set up the properties like this

image

Finally, double click on “Attach File” button, open “Button Properties” dialog and select “Edit Form Code”. FormCode.cs should be open, go to Solution Explorer and add a reference to Microsoft.Sharepoint.dll

Right click on the project Add/New Item…/Code/Class and enter InfoPathAttachmentEncoder.cs for file name. Copy the source code for “Create an Encoder class in Visual Studio” from http://support.microsoft.com/kb/892730 and paste it in the new created file. Make the same for InfoPathAttachmentDecoder.cs

image

Use the following lines of code to upload attached file to Sharepoint document library (“Documents”), plug a hyperlink information into form content and store the form in Form library (“MyForms”)

 

        public void btnAttach_Clicked(object sender, ClickedEventArgs e)
        {
            XPathNavigator attachmentNav = MainDataSource.CreateNavigator();

            // Get the base64 encoded attachment
            string attachedFile = attachmentNav.SelectSingleNode("//my:Attachment", NamespaceManager).Value;
            attachmentNav = attachmentNav.SelectSingleNode("//my:Attachment", NamespaceManager);
            // Delete the encoded file form XML form
            attachmentNav.InnerXml = string.Empty;

            // Get the value of "Title" field
            string reference = MainDataSource.CreateNavigator().SelectSingleNode("//my:Title", NamespaceManager).Value;

            // Convert the base64 encoded string into a byte array
            InfoPathAttachmentDecoder decoder =
              new InfoPathAttachmentDecoder(attachedFile);
            byte[] attachment = decoder.DecodedAttachment;
            string fileName = decoder.Filename;

            // Add the file as an attachment to the SharePoint list item
            using (SPSite site = SPContext.Current.Site)
            {
                if (site != null)
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        // Turn on AllowUnsafeUpdates on the site
                        web.AllowUnsafeUpdates = true;

                        // Add the attached document to "Documents" library
                        // and set the value of "Reference" column to
                        // "Title" filed of the InfoPath form

                        SPFolder lib = web.GetFolder("Documents");
                        SPFile file = lib.Files.Add(fileName, attachment, true);
                        file.Item["Reference"] = reference;
                        file.Item["Title"] = fileName;
                        file.Item.Update();
                        file.Update();

                        // Add attachment file url to "Url" field in the form
                        // by adding extra "my:group4" element in the form

                        XmlDocument xdoc = new XmlDocument();
                        xdoc.LoadXml(MainDataSource.CreateNavigator().OuterXml);

                        Uri url = new Uri(new Uri(web.Url), file.Url);

                        XmlNode grp4 = xdoc.SelectSingleNode("//my:group4", NamespaceManager);
                        XmlNode clonedNode = grp4.CloneNode(true);
                        clonedNode.SelectSingleNode("//my:Url", NamespaceManager).InnerText = url.AbsoluteUri;

                        grp4.ParentNode.InsertAfter(clonedNode, grp4);
                        string formXml = xdoc.OuterXml;
                        // Add the form to "MyForms" library
                        SPFolder formlib = web.GetFolder("MyForms");
                        string formName = string.Format("Request {0}.xml", reference);
                        SPFile form = formlib.Files.Add(formName, Encoding.UTF8.GetBytes(formXml), true);
                        form.Item["Title"] = reference;
                        form.Item.Update();
                        form.Update();

                        // Turn off AllowUnsafeUpdates on the site
                        web.AllowUnsafeUpdates = false;

                        // Close the connection to the site
                        web.Close();

                    }
                    // Close the connection to the site collection
                    site.Close();
                }
            }
        }

After publishing the form and administration approval from CA site I associated the new created Content Type with existing Form library (“MyForms”).

The results:

The initial form’s XML. <my:Attachment> inner text (the encoded file) is very big and Visual Studio hides it from the preview screen

image

The content of <my:Attachment> here is removed

image

The final form’s XML, with attached file URL

image

The empty/new form

image

The final form

image

The uploaded documents

image

The forms library

image

Download complete Visual Studio project

12 comments:

  1. Hi,

    The Solution looks Gr8 but as you have mentioned that the first URL is removed in .xml file and still you have shown 2 different hyperlink under the infopath form.

    We applied this solution and it just store both the attachment on the "Documents" document library but 2 hyperlinks are not displayed and only latest hyperlink was displayed.

    Is this solution work for multiple attachments?

    I have applied and it only shows 1 hyperlink which is the latest one that i attached.

    Please reply me ASAP as i am stuck here.

    Thank you very much for this wonderful post.

    Uday Patel

    ReplyDelete
  2. Hi, the solution works with more than one file attachmen, as is shown on the screenshot. You can download the complete VS2008 projet (I added it to the post just before 2 minutes)

    http://cid-71febae9593b0994.skydrive.live.com/self.aspx/Public%20projects/AttachFilesToInfoPathForm.zip

    ReplyDelete
  3. Hi,

    Thanks for quick response.

    I would like to know that where you have created "Att02" folder under the site?

    Line of code:
    Microsoft.SharePoint.SPFolder formlib = web.GetFolder("Att02");

    As we got some exception on this.

    Thanks again

    Uday Patel

    ReplyDelete
  4. Simply change it to "MyForms", this is the name of your Forms library, where you will store *.xml files
    :-)

    ReplyDelete
  5. Hi.
    I tried your code but I have a problem.
    When I attach files, files are not showed in the repeat table, until I close infopath form and I open again from the form library.

    any solution?

    thanx

    ReplyDelete
  6. Hi @Ruben,
    It works in my example project, which you can find below in the post!

    BR,
    Tihomir

    ReplyDelete
  7. hi,
    i can use your code perfectly.but my problem now is why is the form is keep updating the existing form while i create new form? it replace the old form and the version just keep increase like i'm updating the file.

    Note: I enable minor and major versioning.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. Hi Tihomir,

    Thanks a lot for your step by step code for generating links into Infopath form's body...it works fine when i run it using Infopath client...but when i create as template in Sharepoint and run from Sharepoint as web page..it has problem as mentioned below:
    * It will upload the file but does not remove the file from the attachment and throws an error

    ReplyDelete
  10. Will this solution also work for Sharepoint 2010?

    ReplyDelete
  11. Yes, it will, but take into account there isn't an IP form template project in Visual Studio 2010

    ReplyDelete
  12. Thanks Tihomir for your post. I have a developer question on the Save form portion of your code.

    string formXml = xdoc.OuterXml;
    //Add the form to "Attachment Form" library
    SPFolder formlib = web.GetFolder("Attachment Form");

    I notice if I comment everything below //Add the form... out the form does not save the URL info after opening it back up.

    Is there a work around to not saving the form in the code, but allowing the user to submit later? Thanks again.

    ReplyDelete