Claudio Brotto

June 2008 - Posts

InfoPath Browser Forms in un sito SharePoint aka come fare l’attach di una Master Page

Sarò breve. He he ... forse dal titolo non si direbbe :-)

Provo almeno ad essere schematico.

Esigenza: erogare funzionalità di data entry tramite form infopath renderizzate dal browser (via FormsServices) mantenendo, però, l’aspetto del sito SharePoint. In altre parole, inserire il “content” che IPFS ci mette a disposizione in una pagina che usi la master page del sito SharePoint in questione.

Soluzioni: ce ne sono un po’.

Una, ad esempio, che cito giusto per dovere di cronaca, è quella di usare una PageViewerWebPart che punti all’URL delle pagine aspx di FormsServices. Ammesso e non concesso che utilizzare un IFrame sia realmente necessario :-)

In alternativa (molto meglio a mio parere) si può usare il controllo XmlFormView (Microsoft.Office.InfoPath.Server.Controls.XmlFormView), che abbiamo a disposizione “aggratis”, giacchè alla fine la licenza di IPFS l’abbiamo pagata. E da buon ligure questo è già un motivo sufficiente.

XmlFormView è una web part piuttosto interessante - peraltro utilizzata anche dalle pagine native di IPFS - e dall’uso decisamente immediato. Talmente tanto da rendere possibile la creazione di interfacce semplici anche solamente mediante browser.

Il procedimento è abbastanza lineare, e potrebbe essere questo:

· Aggiunta del controllo suddetto all’elenco di SafeControls per la web application di interesse (un punto in più se decidete di fattorizzare la cosa in ottica di deploy ed inserite la modifica nel manifest di una solution ... anzi no ... due punti)

· Provisioning della web part sulla site collection (Site Settings -> Web Parts -> New -> Selezionare XmlFormView e cliccare su Populate Gallery)

· Si crea una document library dedicata alla memorizzazione delle pagine di data entry (nulla vieta di usarne una preesistente, se non una pura questione di separazione logica dei contenuti)

· Le si associa il content type WebPartPage (non necessario se in fase di creazione della document library abbiamo scelto WebPartPage come template per i nuovi documenti)

· Si crea ... indovinate ? … una WebPartPage :-)

· Si inserisce XmlFormView nella zona che maggiormente ci aggrada

· Si completa la configurazione della web part

Yep. Ma ci sono comunque un paio di considerazioni da tenere in conto.

Una, che vale un po’ sempre, è che ciò che facciamo via browser può arrivare ad un punto di non ritorno, quando le funzionalità out-of-the-box non sono sufficienti (giusto per anticipare la risposta alla domanda “ma come faccio a passare dei parametri alla form ?”).

Questo fa da contrappeso agli (indubbi) vantaggi che ci sono, uno su tutti la possibilità di delegare le operazioni ad un “power user”. Basta saperlo e scegliere di conseguenza.

L’altra considerazione che, invece, prescinde un po’ di più dalle prospettive è legata alle performance pure e semplici.

Una pagina creata come item di una document library è memorizzata in DB. Il che implica un peso non sempre trascurabile per il sistema, che si trova a non poter riutilizzare il codice compilato della pagina ASPX (la stessa cosa che succede per le famose pagine unghosted/customized che si ottengono, ad esempio, modificando un’istanza di pagina con SharePoint Designer).

Logicamente, se si riescono a fattorizzare le funzionalità di una “pagina host con master page”, la strada più conveniente è quella di definire un page template, creando poi istanze della pagina host nei siti di interesse.

Non è così complesso come può sembrare, basta giocare un po’ con le feature ... oops ... Feature di SharePoint 2007.

Ecco qualche riga di codice.

La pagina ASPX, innanzitutto, almeno le “parti salienti”:

<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage" MasterPageFile="~masterurl/default.master" .... />

<%@ Register Assembly="Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.Office.InfoPath.Server.Controls" TagPrefix="ip" %>

<%@ Register Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=Neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebPartPages" TagPrefix="wss" %>

<asp:Content ID="cntMain" runat="server" ContentPlaceHolderID="PlaceHolderMain">

<div>

<ip:XmlFormView ID="frmViewer" runat="server" Height="100%" Width="100%" … />

</div>

</asp:Content>

Manca qualche “piccolo pezzo”, per dirne un il path del form template (o del documento InfoPath). Vi rimando alla documentazione del controllo XmlFormView su MSDN per ogni informazione riguardante i parametri necessari a mostrare documenti InfoPath al suo interno.

Scrivo qui, invece, l’element manifest di una feature che effettua, una volta attivata, il provisioning della page instance:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<Module Path="PageTemplatePathOnFileSystem" Url=”PageInstanceUrl”>

<File Url="FormViewer.aspx" Type="Ghostable"></File>

</Module>

</Elements>

L’attributo Path indica il percorso, sul file system del front-end e relativo alla directory della feature, a partire dal quale verrà copiato il file FormViewer.aspx.

L’attributo URL specifica, invece, il suo percorso relativo alla root del sito web sul quale la feature viene attivata.

Con un po’ di lavoro in più (non molto, alla fine) è infine possibile “parametrizzare” la pagina, valorizzando quindi le proprietà del controllo XmlFormView in maniera dinamica ed ottenendo, alla fine dei giochi, una pagina generica e riutilizzabile.

Voila. Enjoy InfoPath :-)

Dryad, concorrenza e rielaborazione

Visto l'effetto delle traduzioni, non mi cimento e mi limito al copia e incolla.

Dryad is a general-purpose distributed execution engine for
coarse-grain data-parallel applications.

A Dryad application combines computational “vertices” with communication “channels” to form a dataflow graph.

Dryad runs the application by executing the vertices of this graph on a set of available computers, communicating as appropriate through files, TCP pipes, and shared-memory FIFOs.

The vertices provided by the application developer are quite simple and are usually written as sequential programs with no thread creation or locking.

Concurrency arises from Dryad scheduling vertices to run simultaneously on multiple computers, or on multiple CPU cores within a computer.

The application can discover the size and placement of data at run time, and modify the graph as the computation progresses to make efficient use of the available resources.

Potete leggere di più (non molto a dire il vero) sul sito di MSR qui.

Non è certo una scoperta che l'investimento (ricerca, studio, progettazione) sulla programmazione concorrente sia *la* direzione corrente e futura (prossima e forse non tanto prossima).

Quello che è interessante, a mio parere, è il fatto che (almeno dal punto di vista teorico) gli studi e gli esempi di architettura parallela applicata alle tecniche di programmazione datino, a volte, indietro di qualche decade.

Però sfruttare i principi e le idee di tecnologie o soluzioni precedenti non significa non poterle (o doverle ?) rielaborare sulla base dell'avanzamento tecnologico attuale.

E sennò da dove saltava fuori DryadLINQ :-P

Posted: Jun 12 2008, 09:01 PM by devlizard | with 1 comment(s)
Filed under: ,
SPContentDatabase e CLS *non* Compliance

Durante la creazione di uno script PowerShell di "dumping" di una site collection, sono incappato in un errore che mi ha incuriosito.

Nel tentativo di accedere alla property ContentDatabase di un'istanza di SPSite, la shell mi ha risposto picche con il seguente errore:

PS D:\Users\clu> $site.ContentDatabase
out-lineoutput : The field/property: "Id" for type: "Microsoft.SharePoint.Administration.SPContentDatabase" differs only in case from the field/property: "ID". Failed to use non CLS compliant type.
PS D:\Users\clu>

Mmhhh ... senti senti ... vuoi mica dire ...

Per garantire l'operabilità tra linguaggi che targetizzano il .NET Framework, è stato definito un insieme di regole, che vanno sotto il nome di Common Language Specifications.

Rispettando tali regole (non è difficile ... i compilatori ci danno una mano in questi casi) gli autori di librerie possono essere certi che i tipi che definiscono saranno utilizzabili senza problemi da *qualsiasi* linguaggio usi il client.

Per fare un esempio, C# supporta i tipi pointer, che però non sono CLS-Compliant. Inutile esporli pubblicamente se, poi, è richiesta l'interoperabilità :-)

C'è un'altra regola, forse la più nota di tutte, che impone di assegnare nomi differenti ai membri (overload a parte), senza considerare maiuscole e minuscole.

Le verifiche, chiaramente, si applicano solamente all'interfaccia che il tipo espone al chiamante (quindi ai membri public e protected).

In C#, ad esempio, è molto frequente uno stile di codifica che utilizza camelCasing per i campi privati e PascalCasing per le proprietà pubbliche che li incapsulano, e il tutto è perfettamente lecito.

Per curiosità ecco la definizione di SPContentDatabase (snapshots courtesy of Reflector).

SPContentDatabase ha la seguente catena di ereditarietà:

 

Definisce un campo pubblico ID, di tipo System.Guid:

 

Ma SPPersistedObject (tre livelli di ereditarietà sopra) definisce a sua volta un membro pubblico, Id, di tipo System.Guid:

Andando a spulciare il body del getter di SPContentDatabase::Id (minuscolo) ... indovinate un po' che valore ritorna ? (Vedere penultima immagine).

In realtà il caso è ancora più interessante perchè, nella documentazione di SPContentDatabase::ID, il membro è indicato come obsolete. Era presente nell'OM di WSS2.0, ora consigliano di usare la property Id (minuscolo) della classe base.

Dicevo interessante perchè è un esempio delle scelte che si trova ad affrontare chi rilascia la v2 dei propri componenti (a maggior ragione quando gli utilizzatori sono piuttosto numerosi :-)).

Eliminare la proprietà, a questo punto ridondante, da SPContentDatabase sarebbe stata la soluzione più pulita e avrebbe garantito la CLS compliance.

Ma avrebbe di fatto "rotto" la retrocompatibilità.

Ho fatto un giro per leggere quali sono i commenti a riguardo. Qui ad esempio, con annessa indicazione di un modo per circumnavigare il problema. Viene proposto di utilizzare GetMethod per invocare il getter (il metodo get_ID) di SPContentDatabase::ID.

E' chiaro che andando di reflection molte questioni si riescono a risolvere (e se è per quello, ad avere i diritti, si riescono tranquillamente ad invocare metodi privati o a cambiare lo stato interno degli oggetti).

Però ... è proprio una cura del sintomo :-(

Posted: Jun 08 2008, 08:18 AM by devlizard | with no comments
Filed under: ,
Do Less. Get More. Develop on SharePoint :-)

Dal blog del team di SharePoint l'annuncio(*) fresco fresco dell'apertura di un sito (http://www.mssharepointdeveloper.com/) che mette a disposizione risorse introduttive allo sviluppo su piattaforma SharePoint (WSS3/MOSS2007):

There are Whitepapers, Virtual Labs, Web casts, Screen casts, Quickstarts & other resources.  You can also download a SharePoint development VPC with all the tools you need to get started already installed

Al di là del look&feel accattivante, rigorosamente Silverlight-style, direi che è da tenere d'occhio, in particolare per chi si avvicina a SharePoint e ne vuole almeno "assaggiare" qualche aspetto.

(*) Della disponibilità di VSeWSS 1.2 per Visual Studio 2008, altro argomento del post in questione, ha appena scritto Paolo, quindi vi rimando al suo post.

Posted: Jun 05 2008, 12:15 AM by devlizard | with no comments
Filed under:
WordML e Revision ID

Oggi da un cliente, più o meno 12 secondi dopo aver varcato la soglia, mi è stata fatta una domanda che sul momento mi ha lasciato piuttosto perplesso.

"Perchè quando modifico il testo in un documento Word (*) il valore aggiunto non viene semplicemente appeso al termine del precedente, ma tra i due viene frapposto del contenuto ?"

Contestualizzo un minimo.

Il documento in questione è WordML, utilizzato come formato nella generazione (via XSL) di documenti Word a partire da moduli InfoPath (niente document converters, nè OpenXML, nè tanto meno word print view causa necessità di compatibilità con Forms Services).

Il contenuto testuale di origine:

<w:p wsp:rsidR="00E57442" wsp:rsidRDefault="00434E2F">
  <w:r>
    <w:t>Ciao</w:t>
  </w:r>
</w:p>

dopo una semplice modifica, si trasforma in:

<w:p wsp:rsidR="00E57442" wsp:rsidRDefault="00434E2F">
  <w:r>
    <w:t>Ciao</w:t>
  </w:r>
  <w:r wsp:rsidR="00C2519A">
    <w:t> a Tutti</w:t>
  </w:r>
</w:p>

Il che rende *un po' scomodo*, ad esempio, il find and replace di turno :-)

In altri termini, a priori mi sarei aspettato una versione due come segue:

<w:p wsp:rsidR="00E57442" wsp:rsidRDefault="00434E2F">
  <w:r>
    <w:t>Ciao a tutti</w:t>
  </w:r>
</w:p>

Il motivo di questo comportamento, almeno per me, inaspettato è spiegato nel dettaglio in questo post di Brian Jones.

Ne riassumo in due righe il significato, rimandando ogni approfondimento direttamente alla fonte.

Word memorizza dei revision identifiers (RSIDs), indipendentemente dal fatto che la funzionalità di track changes sia o meno abilitata sul documento in questione. Tali id sono numeri casuali e non contengono, per ragioni di sicurezza, alcuna indicazione che consenta di risalire all'utente che ha apportato le modifiche. Risultano utili nel caso di fork delle modifiche sui documenti, poichè eventuali merge successivi si riducono ad una indagine sulle revisioni (memorizzate come attributo nel documento stesso).

Bello. E se non lo voglio ?

Dalle opzioni del Trust Center (sia in Word 2003 che in Word 2007), Privacy Options e deselezione del check "Store random numbers to improve Combine accuracy".

Credits a Vittorio e a Mattia per la segnalazione (e per l'aver individuato in tempi brevi il workaround :-P).