giovedì 30 settembre 2010

Come creare una form Infopath che decida a runtime la Save Location

Titolo un po' criptico ma richiama una problematica che ho dovuto affrontare in questi giorni. Dovevo creare una form InfoPath da usare via browser in SharePoint 2010, che salvasse il suo contento in una raccolta documentale in formato XML; niente di difficile direte voi, basta creare una Data Connection verso la lista SharePoint in cui si vuole salvare il risultato. Esatto, ma la mia document library stava in un sito che andava salvato in un template. Come nella maggior parte delle volte, niente programmazione, niente site definition; tutto creato da browser e con InfoPath 2010 Designer. Dopo una giornata di tentativi (premetto che le mie conoscenze di InfoPath tendevano a meno infinito) leggendo nell'event viewer del server ho scoperto come viene invocata la form InfoPath e da questo, aggiungendo alcuni consigli di Alberto Ballabio (trovati sul suo sito SGART)  e di Massimo Luinetti e un pizzico di aiuto reperito da questo articolo ho scritto del codice per risolvere il mio problema.
Innanzi tutto cosa ho trovato nell'event viewer. Quando creo la form la url invocata è del tipo:
http://<ServerName>/_layouts/FormServer.aspx?XsnLocation=http://srvsp01/FormServerTemplates/formName.xsn&SaveLocation=http://<ServerName>/nav/temp/Changes&Source=http://<ServerName>/nav/temp/Changes/Forms/AllItems.aspx&DefaultItemOpen=1
Quello che ci interessa è il parametro SaveLocation.
Quando riapro il form invece la url invocata è:
http://<ServerName>/_layouts/FormServer.aspx?XmlLocation=/nav/temp/Changes/formName.xml&Source=http://<ServerName>/nav/temp/Changes/Forms/AllItems.aspx&DefaultItemOpen=1
Quindi le due url non sono uguali. Detto questo la soluzione prevede di cambiare a runtime la url della Data Connection di tipo Submit. Anche in questo caso c'è l'inghippo. Infatti ho scoperto a mie spese, che l'unico evento in grado di modificare con successo la url della Data Connection è l'evento di Loading.
Ecco il codice finito e funzionante.
using Microsoft.Office.InfoPath;
using System;
using System.Xml;
using System.Xml.XPath;

namespace caricafiles
{
    public partial class FormCode
    {
        private object _strUri
        {
            get
            {
                return FormState["_strUri"];
            }
            set
            {
                FormState["_strUri"] = value;
            }
        }


        // NOTE: The following procedure is required by Microsoft Office InfoPath.
        // It can be modified using Microsoft Office InfoPath.
        public void InternalStartup()
        {
            EventManager.FormEvents.Loading += new LoadingEventHandler(FormEvents_Loading);
        }

        public void FormEvents_Loading(object sender, LoadingEventArgs e)
        {
            Init(e);       
        }

        // Modificare la url della data connection è possibile solo nel metodo FormEvents_Loading
        private void Init(LoadingEventArgs e)
        {
            //Get the Uri (or SaveLocation in a browser form) of where 
            //the form was opened 
            //See if the form was opened in the browser
            Boolean OpenedInBrowser = Application.Environment.IsBrowser;
            //Get a reference to the submit data connection
            FileSubmitConnection fc = (FileSubmitConnection)this.DataConnections["FOB Save"];

            //If so, we will get the "SaveLocation" from the InputParameters
            if (OpenedInBrowser)
            {
                if (e.InputParameters.ContainsKey("SaveLocation"))
                {
                    _strUri = e.InputParameters["SaveLocation"].ToString();
                }
                else if (e.InputParameters.ContainsKey("Source"))
                {
                    string source = e.InputParameters["Source"].ToString();
                    _strUri = source.Substring(0, source.IndexOf("Forms") - 1);
                }
                else
                {
                    _strUri = _strUri;
                }
            }
            else
            {
                //If it was opened in the client, we will get the Uri
                _strUri = this.Template.Uri.ToString();
            }

            //Modify the URL we want to submit
            fc.FolderUrl = (string)_strUri;
        }
    }
}
Ultima nota: tenete in considerazione la lingua di InfoPath client quando andate a cercare aiuto un internet perchè i nodi della form InfoPath vengono localizzati e quindi il codice XPath di un nodo di una form in inglese risulta diverso da quello di una form in italiano.